FrankKai / FrankKai.github.io

FE blog
https://frankkai.github.io/
362 stars 39 forks source link

DOM进阶之querySelector和querySelectorAll #191

Open FrankKai opened 4 years ago

FrankKai commented 4 years ago

之所以写这篇博文,是因为在写热力图的过程中,需要匹配一组class以foo-bar-xxx开头的元素,然后对为这组元素增加自定义事件监听器。 然而在匹配一组class以foo-bar-xxx开头的元素这一步我就卡住了,虽然知道有多种查询DOM节点的方法,但是并不清楚到底用哪一个,查找资料后通过 querySelectorAll和属性选择器匹配到了元素。但是对于这两个方法还是一知半解,所以开此issue性系统学习一下。

image

this.tableCells = document.querySelectorAll('td[class^="custom-alpha"]');

关于实际的应用,可以查阅这篇文章:如何为DOM创建自定义事件?

使用选择器定位DOM元素

querySelector和querySelectorAll之父 NodeSelector interface 规范为Document,DocumentFragment或Element增加了2个新方法:

querySelector和querySelectorAll的区别

如果对Element和NodeList不理解的话,可以查看DOM进阶之Element和NodeList

需要特别注意一点: document.querySelectorAll()得到的NodeList是static nodelist,意味着如果DOM发生变化,查询到的集合不会更新。而其他的DOM查询方法是可以的。因为它们查询到的是live nodelist。

选择器语法

选择器可以接收一个或多个用逗号分隔的选择器。

选择css class为warning或note的所有p元素:

const pTagAll = document.querySelectorAll("p.warning, p.note");

选择id为main,basic,exclamation中一个的元素:

const idTag = document.querySelector("#main, #basic, #exclamation");

选择器有哪些?

是所有css选择器。注意是css选择器。

image https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Selectors

querySelector()

querySelector返回匹配器匹配到的第一个元素。

const element = baseElement.querySelector(selectors);
const element = document.querySelector(selectors);

匹配到谁?

匹配到baseElement的第一个后代元素。

const el = document.body.querySelector("style[type='text/css'], style:not([type])");
<div>
  <h5>Original content</h5>
  <p> inside paragraph<span>inside span</span>inside paragraph</p>
</div>
<div>
  <h5>Output</h5>
  <div id="output"></div>
</div>
const baseElement = document.querySelector("p");
document.getElementById("output").innerHTML = (baseElement.querySelector("div span").innerHTML);

子元素可以通过querySelector找到上级的元素吗?

亲测不可以。

因此可以将querySelector理解为从上往下找。若不需要指定特殊的baseElement,可以直接通过document查找。

Document.querySelector与Element.querySelector区别是什么?

Document继承自HTMLElement,而HTMLElement又继承自Element,所以本质上查询方法一样。 但是我们这里将根浏览器根节点当做Document,根节点的子节点当做Element,所以区别的话那就是查询范围不同,Document.querySelector作用域整个文档所有后代节点,而Element.querySelector作用域选中元素的后代节点。

<html>
    <body>
        <div>
            <p>Hello World.</p>
            <ul>
                 <li>foo</li>
                 <li>bar</li> 
                 <li>baz</li>
            </ul>
        </div>     
    </body>
</html>

Document.querySelector查询所有html,body等等全部子元素。 Element为div时,Element.querySelector查询p,ul,li等等子元素。

querySelectorAll()

querySelectorAll()通过选择器匹配元素的后代节点,返回一个static(not live)NodeList。

const element = baseElement.querySelectorAll(selectors);
const element = document.querySelectorAll(selectors);

#### 可以选择伪元素吗?
如果选择器包含伪元素,返回的是空。

#### 如何选中所有以某某前缀开头的元素?
通过属性选择器和querySelectorAll配合。
```html
<section class="box" id="sect1">
  <div class="funnel-chart-percent1">10.900%</div>
  <div class="funnel-chart-percent2">3700.00%</div>
  <div class="funnel-chart-percent3">0.00%</div>
</section>
const refs = document.querySelectorAll(`[data-name*="funnel-chart-percent"]`);

NodeList是一个普通的数组吗?

不是。 它没有slice,some,map等方法,但是可以通过for循环,forEach等多种方式遍历。可以查阅:DOM进阶之Element和NodeList

限制选择器作用域的:scope伪元素是什么?

<div class="outer">
  <div class="select">
    <div class="inner">
    </div>
  </div>
</div>

错误匹配出不属于自己的子类。

var select = document.querySelector('.select');
var inner = select.querySelectorAll('.outer .inner');
inner.length; // 1, not 0!

这是因为默认情况下,querySelectorAll() 只验证选择器中最后一个元素是不是在选择范围内。

如何解决这个问题呢?通过:scope去限定搜索的作用域,使得querySelectorAll仅仅匹配baseElement的后代节点。

var select = document.querySelector('.select');
var inner = select.querySelectorAll(':scope .outer .inner');
inner.length; // 0

参考资料: https://developer.mozilla.org/en-US/docs/Web/API/Document_object_model/Locating_DOM_elements_using_selectors https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelector https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelectorAll https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll