image.png

定位/获取节点

获取DOM节点

1、通过document.getElement方法

  1. document.getElementById("id"); //通过id获取可以直接定位唯一的一个DOM节点
  2. document.getElementsByClassName("aBC"); //通过类名获取,可能返回一组
  3. document.getElementsByClassName(某个节点,"b"); //从某个节点开始,通过类名获取,可能返回一组
  4. let allElements = document.getElementsByTagName("*");// 所有元素
  5. let images = document.getElementsByTagName("img"); //通过标签名获取,返回数组
  6. // 返回包含零个或多个元素的 NodeList。
  7. // 在 HTML 文档中,这个方法返回一个HTMLCollection 对象。
  8. // 考虑到二者都是“实时”列表,HTMLCollection 与 NodeList 是很相似的
  9. alert(images.length); // 图片数量
  10. alert(images[0].src); // 第一张图片的 src 属性
  11. alert(images.item(0).src); // 同上
  12. // <img src="myimage.gif" name="myImage">
  13. let myImage = images.namedItem("myImage");// 根据名字获取引用
  14. let myImage = images["myImage"];

2、通过节点关系

  1. xxx.children;// 获取节点test下的所有直属子节点
  2. test.childNodes;// 获取节点test下的所有子节点,包括空格、换行符
  3. xxx.children[0];//第一个个子节点
  4. test.firstElementChild;// 获取节点test下第一个子节点:
  5. test.lastElementChild;// 获取节点test下最后一个子节点:
  6. test.parentNode;//父节点

3、通过CSS选择器

  1. var js = document.getElementById('test-p');
  2. // 通过querySelectorAll获取class名为c-red节点内的符合条件的所有<P>节点:Html5
  3. var arr = document.getElementsByClassName('c-red')[1].getElementsByTagName('p');
  4. var js = document.querySelector('#test-p');
  5. var arr = document.querySelectorAll('.c-red.c-green>p');

4、通过HTML对象选择器

  1. var p = document.forms["frm1"]; //id为frm1的表单forms
  2. var p = document.forms.frm1;

document.anchors 包含文档中所有带 name 属性的元素。
document.forms 包含文档中所有

元素(与 document.getElementsByTagName (“form”)返回的结果相同)。
document.images 包含文档中所有JS - 操作DOM - 图2元素(与 document.getElementsByTagName (“img”)返回的结果相同)。
document.links 包含文档中所有带 href 属性的
元素。

5、通过Selectors API(推荐)

通过浏览器原生支持这个 API,解析和遍历 DOM 树可以通过底层编译语言实现,性能也有 了数量级的提升。

Selectors API Level 1 的核心是两个方法:querySelector()和 querySelectorAll()。

在兼容浏 览器中,Document 类型和 Element 类型的实例上都会暴露这两个方法。

Selectors API Level 2 规范在 Element 类型上新增了更多方法,比如 matches()、find()和 findAll()。

不过,目前还没有浏览器实现或宣称实现 find()和 findAll()。

选择第一个元素 querySelector( )
在 Document 上使用 querySelector()方法时,会从文档元素开始搜索;
在 Element 上使用 querySelector()方法时,则只会从当前元素的后代中查询。
只选择第一个元素。

  1. // 取得<body>元素
  2. let body = document.querySelector("body");
  3. let div = body.querySelector("div"); // 在body子元素中取得第一个div元素
  4. // 取得 ID 为"myDiv"的元素
  5. let myDiv = document.querySelector("#myDiv");
  6. // 取得类名为"selected"的第一个元素
  7. let selected = document.querySelector(".selected");
  8. // 取得类名为"button"的图片
  9. let img = document.body.querySelector("img.button");

选择所有元素 querySelectorAll( )
在 Document 上使用 querySelector()方法时,会从文档元素开始搜索;
在 Element 上使用 querySelector()方法时,则只会从当前元素的后代中查询。
选择匹配的所有元素,返回Nodelist对象。
但它是一 个静态的“快照”,而非“实时”的查询。这样的底层实现避免了使用 NodeList 对象可能造成的性 能问题。

  1. // 取得 ID 为"myDiv"的<div>元素中的所有<em>元素
  2. let ems = document.getElementById("myDiv").querySelectorAll("em");
  3. // 取得所有类名中包含"selected"的元素
  4. let selecteds = document.querySelectorAll(".selected");
  5. // 取得所有是<p>元素子元素的<strong>元素
  6. let strongs = document.querySelectorAll("p strong");
  7. //返回的 NodeList 对象可以通过 for-of 循环、item()方法或中括号语法取得个别元素。比如:
  8. let strongElements = document.querySelectorAll("p strong");
  9. // 以下 3 个循环的效果一样
  10. for (let strong of strongElements) {
  11. strong.className = "important";
  12. }
  13. for (let i = 0; i < strongElements.length; ++i) {
  14. strongElements.item(i).className = "important";
  15. }
  16. for (let i = 0; i < strongElements.length; ++i) {
  17. strongElements[i].className = "important";
  18. }

=================

添加节点

在该DOM节点下新增一个子节点,相当于动态增加了一个HTML节点;

1、createTextNode 创建

  1. //创建文本节点,可以插入到其他节点里面
  2. var node = document.createTextNode("这是新文本。");

2、appendChild 追加

在最后插入,如果元素在前面已存在,会转移到新位置(删除原来位置的)

  1. var js = document.getElementById('js');
  2. var list = document.getElementById('list');
  3. list.appendChild(js);
  4. //先创建再插入
  5. var list = document.getElementById('list'),
  6. var haskell = document.createElement('p');
  7. haskell.id = 'haskell';
  8. haskell.innerText = 'Haskell';
  9. list.appendChild(haskell);
  10. //甚至可以插入css样式
  11. var d = document.createElement('style');
  12. d.setAttribute('type', 'text/css'); //设置属性(属性名,属性值)
  13. d.innerHTML = 'p { color: red }';
  14. document.getElementsByTagName('head')[0].appendChild(d);

3、insertBefore 插入

在特定位置前面插入

  1. let haskell = document.createElement('p');
  2. list.insertBefore(haskell, js);//在js前面插入haskell
  3. list.insertBefore(haskell, null);// 等同于追加,就是在最后面插入

4、cloneNode 拷贝

cloneNode()方法接收一个布尔值参数,表示是否深复制。

如果传入 true ,会进行深复制,即复制节点及其整个子 DOM 树。
如果传入 false,则只会复制调用该方法的节点。

cloneNode()方法不会复制添加到 DOM 节点的 JavaScript 属性,比如事件处理程序。
这个方法只复制 HTML 属性,以及可选地复制子节点。除此之外则一概不会复制。

  1. let myList = document.getElementById('a');
  2. /*
  3. 假设为
  4. <ul>
  5. <li>item 1</li>
  6. <li>item 2</li>
  7. <li>item 3</li>
  8. </ul>
  9. */
  10. // 深拷贝,复制了ul标签和里面的li标签
  11. let deepList = myList.cloneNode(true);
  12. alert(deepList.childNodes.length); // 3(IE9 之前的版本)或 7(其他浏览器)
  13. // 浅拷贝,只复制了ul标签,里面的li没复制
  14. let shallowList = myList.cloneNode(false);
  15. alert(shallowList.childNodes.length); // 0

=================

删除节点

将该节点从HTML中删除,相当于删掉了该DOM节点的内容以及它包含的所有子节点。

  1. var self = document.getElementById('to-be-removed'); //要删除的节点
  2. var parent = self.parentElement; //获取要删除节点的父节点
  3. // 或
  4. var parent = self.parentNode; //获取要删除节点的父节点
  5. var removed = parent.removeChild(self); //用父节点调用删除方法删除

注意到删除后的节点虽然不在文档树中了,但其实它还在内存中,可以随时再次被添加到别的位置。

=================

改变节点

更新该DOM节点的内容,相当于更新了该DOM节点表示的HTML的内容;

1、innerHTML 属性

用innerHTML 属性修改,甚至可以替换内部的子节点

  1. var p = document.getElementById('p-id');// 获取<p id="p-id">...</p>
  2. p.innerHTML = 'ABC'; // <p id="p-id">ABC</p>
  3. p.innerHTML = 'ABC <span style="color:red">RED</span> XYZ';// <p>...</p>的内部结构已被替换

2、replaceChild 替换

  1. var para = document.createElement("p");
  2. var parent = document.getElementById("div1");
  3. var child = document.getElementById("p1");
  4. parent.replaceChild(para, child);//替换

3、textContent 内容

  1. var p = document.getElementById('p-id');
  2. p.innerText = '<script>alert("Hi")</script>';
  3. p.textContent = '<script>alert("Hi")</script>';
  4. // HTML被自动编码,无法设置一个<script>节点:
  5. // <p id="p-id"><script>alert("Hi")</script></p>

用innerHTML时要注意,是否需要写入HTML。如果写入的字符串是通过网络拿到了,要注意对字符编码来避免XSS攻击。

innerHTML 返回 HTML 文本。通常,为了在元素中检索或写入文本,人们使用innerHTML。但是,textContent通常具有更好的性能,因为文本不会被解析为HTML。此外,使用textContent可以防止 XSS 攻击。

4、write / writeIn 写入

只能简单的写入文本

  1. document.write(p); //在页面最下方追加
  2. document.writeIn(p); //在页面最下方追加,并且加一个换行符(\n)。

write()和 writeln()方法经常用于动态包含外部资源,如 JavaScript 文件。

  1. <html>
  2. <head>
  3. <title>document.write() Example</title>
  4. </head>
  5. <body>
  6. <script type="text/javascript">
  7. document.write("<script type=\"text/javascript\" src=\"file.js\">" + "<\/script>");
  8. </script>
  9. </body>
  10. </html>

如果是在页面加载完之后再调用 document.write(),则输出的内容会重写整个页面

  1. <html>
  2. <head>
  3. <title>document.write() Example</title>
  4. </head>
  5. <body>
  6. <p>这里的内容会被覆盖</p>
  7. <script type="text/javascript">
  8. window.onload = function(){
  9. document.write("Hello world!");
  10. };
  11. </script>
  12. </body>
  13. </html>

这个例子使用了 window.onload 事件处理程序,将调用 document.write()的函数推迟到页面加载完毕后执行。执行之后,字符串”Hello world!”会重写整个页面内容。

严格的 XHTML 文档不支持文档写入。对于内容类型为 application/xml+xhtml的页面,这些方法不起作用。

5、改变类 classList

要操作类名,可以通过 className 属性实现添加、删除和替换。

HTML5 通过给所有元素增加 classList 属性为这些操作提供了更简单也更安全的实现方式。

classList 是一个新的集合类型 DOMTokenList 的实例。

与其他 DOM 集合类型一样,DOMTokenList 也有 length 属性表示自己包含多少项,也可以通过 item()或中括号取得个别的元素。

  1. // <div class="bd user disabled">...</div>
  2. let div = document.getElementsByClassName("bd user disabled");
  3. // 删除"disabled"类
  4. div.classList.remove("disabled");
  5. // 添加"current"类
  6. div.classList.add("current");
  7. // 切换"user"类,如果类名列表中已经存在"user",则删除;如果不存在,则添加
  8. div.classList.toggle("user");
  9. // 检测类名,contains表示给定的 类名 是否存在
  10. if (div.classList.contains("bd") && !div.classList.contains("disabled")){
  11. // 执行操作
  12. )
  13. // 迭代类名
  14. for (let class of div.classList){
  15. doStuff(class);
  16. }

=================

遍历节点

遍历该DOM节点下的子节点,以便进行进一步操作;如上面的insertBefore,一般要遍历插入元素的子节点

1、通过 children

  1. var i, c,list = document.getElementById('list');
  2. for (i = 0; i < list.children.length; i++) {
  3. c = list.children[i]; // 拿到第i个子节点
  4. }

2、通过 Element Traversal

IE9 之前的版本不会把元素间的空格当成空白节点,而其他浏览器则会。

这样就导致了 childNodes 和 firstChild 等属性上的差异。为了弥补这个差异,同时不影响 DOM 规范,W3C 通过新的 Element Traversal 规范定义了一组新属性。

在支持的浏览器中,所有 DOM 元素都会有这些属性,为遍历 DOM 元素提供便利。这样开发者就 不用担心空白文本节点的问题了。

  1. let parentElement = document.getElementById('parent');
  2. // 1、返回子元素数量
  3. let childNum = parentElement.childElementCount;
  4. // 2、指向第一个 Element 类型的子元素(Element 版 firstChild);
  5. let currentChildElement = parentElement.firstElementChild;
  6. // 循环遍历,没有子元素,firstElementChild 返回 null,跳过循环
  7. while (currentChildElement) {
  8. // 3、指向最后一个 Element 类型的子元素(Element 版 lastChild);
  9. if (currentChildElement === parentElement.lastElementChild) {
  10. break;
  11. }
  12. // 4、指向后一个 Element 类型的同胞元素(Element 版 nextSibling)
  13. currentChildElement = currentChildElement.nextElementSibling;
  14. // 5、指向前一个 Element 类型的同胞元素( Element 版previousSibling);
  15. let x = currentChildElement.previousElementSibling;
  16. }

3、通过 NodeIterator

https://developer.mozilla.org/zh-CN/docs/Web/API/NodeIterator

  1. // 创建节点遍历迭代器
  2. // root,作为遍历根节点的节点。
  3. // whatToShow,数值代码,表示应该访问哪些节点。
  4. // filter,NodeFilter 对象或函数,表示是否接收或跳过特定节点。如不需要可以传null
  5. var nodeIterator = document.createNodeIterator(root, whatToShow, filter);
  6. // NodeIterator 的两个主要方法是 nextNode() 下一个节点 和 previousNode()上一个节点
  7. let node = iterator.nextNode(); // 第一次使用,表示根节点root
  8. while (node !== null) {
  9. console.log(node.tagName); // 输出标签名
  10. node = iterator.nextNode();
  11. }
  12. // 可以传入过滤器,表示哪些节点是我想要的
  13. let filter = function(node) {
  14. return node.tagName.toLowerCase() == "li" ?
  15. NodeFilter.FILTER_ACCEPT : // 表示该节点是我要的
  16. NodeFilter.FILTER_SKIP; // 表示跳过这个节点
  17. };

4、通过 TreeWalker

TreeWalker 是 NodeIterator 的高级版,除了包含同样的 nextNode()、previousNode()方法, TreeWalker 还添加了如下在 DOM 结构中向不同方向遍历的方法。
parentNode(),遍历到当前节点的父节点。
firstChild(),遍历到当前节点的第一个子节点。
lastChild(),遍历到当前节点的最后一个子节点。
nextSibling(),遍历到当前节点的下一个同胞节点。
previousSibling(),遍历到当前节点的上一个同胞节点。

  1. treeWalker = document.createTreeWalker(root, whatToShow, filter);// 参考NodeIterator

节点过滤器(filter)除了可以返回 NodeFilter.FILTER_ACCEPT 和 NodeFilter. FILTER_SKIP,还可以返回 NodeFilter.FILTER_REJECT。
在使用 NodeIterator 时,
NodeFilter. FILTER_SKIP 和 NodeFilter.FILTER_REJECT 是一样的。

但在使用 TreeWalker 时,NodeFilter. FILTER_SKIP 表示跳过节点,访问子树中的下一个节点,
而 NodeFilter.FILTER_REJECT 则表示跳 过该节点以及该节点的整个子树。

相比于 NodeIterator,TreeWalker 类型为遍历 DOM 提供了更大的灵活性。

=================

节点属性

1、获取样式值

  1. //方法一:元素.currentStyle.样式
  2. box1.currentStyle.width;
  3. //但是auto还是显示auto,且只有ie支持
  4. //方法二:getComputedStyle(元素,伪元素);
  5. var x = getComputedStyle(y,null);
  6. x.width;
  7. x["width"];
  8. //推荐,都支持,而且不会显示,不支持ie8以下

2、获取属性 getAttribute( )

image.png
也能取得不是 HTML 语言正式属性的自定义属性的值,因此主要用于获取自定义属性。

  1. let div = document.getElementById("myDiv");
  2. alert(div.getAttribute("id")); // "myDiv"
  3. alert(div.getAttribute("class")); // "bd"
  4. alert(div.getAttribute("title")); // "Body text"
  5. alert(div.getAttribute("lang")); // "en"
  6. alert(div.getAttribute("dir")); // "ltr"
  7. //<div id="myDiv" my_special_attribute="hello!"></div> 自定义一个属性
  8. let value = div.getAttribute("my_special_attribute"); // 获取了自定义属性

3、设置属性 setAttribute( )

推荐,因为移植性好
image.png
改的是临时的,从F12里面看是更改之前的。这个和DOM工作原理有关

尽量通过修改class值改变属性,而不是通过js直接改属性

接收两个参数:要设置的属性名和属性的值。
如果属性已经存在,则 setAttribute()会以指定的值替换原来的值;
如果属性不存在,则 setAttribute()会以指定的值创建该属性。

  1. div.setAttribute("id", "someOtherId");
  2. div.setAttribute("class", "ft");
  3. div.setAttribute("title", "Some other text");
  4. div.setAttribute("lang","fr");
  5. div.setAttribute("dir", "rtl");

5、移除HTML属性 removeAttribute

不单单是清除属性的值,而是会把整个属性完全从元素中去掉

  1. div.removeAttribute("class");

6、直接设置 style

  1. var p = document.getElementById('p-id');
  2. p.style.color = '#ff0000';
  3. p.style.fontSize = '20px';
  4. p.style.paddingTop = '2em';

7、设置属性 element.属性

旧方式
image.png
image.png

=================

DOM选择范围

DOM2 在 Document 类型上定义了一个 createRange()方法,暴露在 document 对象上。

使用这 个方法可以创建一个 DOM 范围对象,创建范围并指定它的位置之后,可以对范围的内容执行一些 操作,从而实现对底层 DOM 树更精细的控制。

  1. let range = document.createRange();
  2. range.startContainer; // 范围起点所在的节点(选区中第一个子节点的父节点)。
  3. range.startOffset; //范围起点在 startContainer 中的偏移量。
  4. // 如果 startContainer 是文本节点、注释节点或 CData 区块节点,则 startOffset 指范围起点之前跳过的字符数;
  5. // 否则,表示范围中第一个节点的索引。
  6. range.endContainer;// 范围终点所在的节点(选区中最后一个子节点的父节点)。
  7. range.endOffset;// 范围起点在 startContainer 中的偏移量(与 startOffset 中偏移量的含义相同)。
  8. range.commonAncestorContainer;// 文档中以startContainer和endContainer为后代的最深的节点。

https://developer.mozilla.org/zh-CN/docs/Web/API/Range

=================

节点事件

阻止原本事件
image.png