JavaScript 节点增删改查
查找节点
前面四个方法除了定义在 document 对象上,还定义在 Element 上,即在元素节点上也可以调用。
querySelector()
document.querySelector
方法接受一个 CSS 选择器作为参数,返回匹配该选择器的元素节点。如果有多个节点满足匹配条件,则返回第一个匹配的节点。如果没有发现匹配的节点,则返回null
。
let el1 = document.querySelector('.myclass');
let el2 = document.querySelector('#myParent > [ng-click]');
Since HTML5 it’s been valid to start an HTML element ID with a number. For example <div id="10">
. From an HTML perspective, that’s fine.
However that doesn’t mean that CSS is happy to have an ID selector starting with a number. For example, this will not work:
#10 {
color: red; /* does not work */
}
That’s because even though HTML5 is quite happy for an ID to start with a number, CSS is not. CSS simply doesn’t allow selectors to begin with a number. See more at W3C Specification.
Similarly, the querySelector
doesn’t allow selectors to begin with a number.
document.querySelectorAll('#10');
// Uncaught DOMException: Failed to execute 'querySelectorAll' on 'Document': '#10' is not a valid selector.
However, you can easily work around this by using an attribute selector:
document.querySelector('[id="10"]');
querySelectorAll()
document.querySelectorAll
方法与 querySelector
用法类似,区别是返回一个 NodeList
对象,包含所有匹配给定选择器的节点。
let elementList = document.querySelectorAll('.myclass');
这两个方法的参数,可以是逗号分隔的多个 CSS 选择器,返回匹配其中一个选择器的元素节点,这与 CSS 选择器的规则是一致的。
let matches = document.querySelectorAll('div.note, div.alert');
上面代码返回 class 属性是 note 或 alert 的 div 元素。
这两个方法都支持复杂的 CSS 选择器。
// 选中 data-foo-bar 属性等于 someval 的元素
document.querySelectorAll('[data-foo-bar="someval"]');
// 选中 myForm 表单中所有不通过验证的元素
document.querySelectorAll('#myForm :invalid');
// 选中div元素,那些 class 含 ignore 的除外
document.querySelectorAll('DIV:not(.ignore)');
// 同时选中 div,a,script 三类元素
document.querySelectorAll('DIV, A, SCRIPT');
如果 querySelectorAll
方法的参数是字符串 *
,则会返回文档中的所有元素节点。
它们不支持 CSS 伪元素的选择器(比如 :first-line
和 :first-letter
)和伪类的选择器(比如 :link
和 :visited
),即无法选中伪元素和伪类。
querySelectorAll
的返回结果不是动态集合,不会实时反映元素节点的变化。
getElementsByTagName()
document.getElementsByTagName
方法搜索 HTML 标签名,返回符合条件的元素。如果没有任何匹配的元素,就返回一个空集。
它的返回值是一个类似数组对象( HTMLCollection 实例)
可以实时反映 HTML 文档的变化。
let paras = document.getElementsByTagName('p');
paras instanceof HTMLCollection; // true
上面代码返回当前文档的所有 p 元素节点。
HTML 标签名是大小写不敏感的,因此 getElementsByTagName
方法也是大小写不敏感的。
返回结果中,各个成员的顺序就是它们在文档中出现的顺序。
如果传入 *
,就可以返回文档中所有 HTML 元素。
let allElements = document.getElementsByTagName('*');
getElementsByClassName()
document.getElementsByClassName
方法返回包括了所有 class 名字 符合指定条件的元素,元素的变化实时反映在返回结果中。
它的返回值是一个类似数组对象( HTMLCollection 实例)
可以实时反映 HTML 文档的变化。
let elements = document.getElementsByClassName(names);
由于 class 是保留字,所以 JavaScript 一律使用 className
表示 CSS 的 class。
参数可以是多个 class,它们之间使用空格分隔。
let elements = document.getElementsByClassName('foo bar');
上面代码返回同时具有 foo 和 bar 两个 class 的元素,foo 和 bar 的顺序不重要。
正常模式下,CSS 的 class 是大小写敏感的。(quirks mode 下,大小写不敏感。)
document.getElementsByName()
document.getElementsByName
方法用于选择拥有 name
属性的 HTML 元素(比如 <form>
、<radio>
、<img>
、<frame>
、<embed>
和 <object>
等),返回一个类似数组的的对象(NodeList 实例),因为 name 属性相同的元素可能不止一个。
// 表单为 <form name="x"></form>
var forms = document.getElementsByName('x');
forms[0].tagName; // "FORM"
document.getElementById()
document.getElementById
方法返回匹配指定 id 属性的元素节点。如果没有发现匹配的节点,则返回 null
。
var elem = document.getElementById('para1');
该方法的参数是大小写敏感的。
Element
后面没有 s
。
document.getElementById
方法与 document.querySelector
方法都能获取元素节点,不同之处是 document.querySelector
方法的参数使用 CSS 选择器语法,document.getElementById
方法的参数是元素的 id 属性值。
document.getElementById('myElement');
document.querySelector('#myElement');
上面代码中,两个方法都能选中 id 为 myElement 的元素
document.getElementById()
比 document.querySelector()
效率高得多。
增加节点
Node.appendChild()
appendChild
方法接受一个节点对象作为参数,将其作为最后一个子节点,插入当前节点。该方法的返回值就是插入文档的子节点。
var p = document.createElement('p');
document.body.appendChild(p);
上面代码新建一个 <p>
节点,将其插入 document.body
的尾部。
如果参数节点是 DOM 已经存在的节点,appendChild
方法会将其从原来的位置,移动到新位置。
var element = document.createElement('div').appendChild(document.createElement('b'));
上面代码的返回值是 <b></b>
,而不是 <div></div>
。
如果 appendChild 方法的参数是 DocumentFragment 节点,那么插入的是 DocumentFragment 的所有子节点,而不是 DocumentFragment 节点本身。返回值是一个空的 DocumentFragment 节点。
document.createElement()
document.createElement
方法用来生成元素节点,并返回该节点。
var newDiv = document.createElement('div');
createElement
方法的参数为元素的标签名,即元素节点的 tagName
属性,对于 HTML 网页大小写不敏感,即参数为 div 或 DIV 返回的是同一种节点。如果参数里面包含尖括号(即 <
和 >
)会报错。
document.createElement('<div>');
// DOMException: The tag name provided ('<div>') is not a valid name
document.createElement
的参数可以是自定义的标签名。
document.createElement('foo');
document.createTextNode()
document.createTextNode
方法用来生成文本节点(Text 实例),并返回该节点。它的参数是文本节点的内容。
var newDiv = document.createElement('div');
var newContent = document.createTextNode('Hello');
newDiv.appendChild(newContent);
上面代码新建一个 div 节点和一个文本节点,然后将文本节点插入 div 节点。
这个方法可以确保返回的节点,被浏览器当作文本渲染,而不是当作 HTML 代码渲染。因此,可以用来展示用户的输入,避免 XSS 攻击。
var div = document.createElement('div');
div.appendChild(document.createTextNode('<span>Foo & bar</span>'));
console.log(div.innerHTML);
// <span>Foo & bar</span>
上面代码中,createTextNode
方法对大于号和小于号进行转义,从而保证即使用户输入的内容包含恶意代码,也能正确显示。
需要注意的是,该方法不对单引号和双引号转义,所以不能用来对 HTML 属性赋值。
function escapeHtml(str) {
var div = document.createElement('div');
div.appendChild(document.createTextNode(str));
return div.innerHTML;
}
var userWebsite = '" onmouseover="alert(\'derp\')" "';
var profileLink = '<a href="' + escapeHtml(userWebsite) + '">Bob</a>';
var div = document.getElementById('target');
div.innerHTML = profileLink;
// <a href="" onmouseover="alert('derp')" "">Bob</a>
上面代码中,由于 createTextNode
方法不转义双引号,导致 onmouseover
方法被 注入了代码。
document.createAttribute()
document.createAttribute
方法生成一个新的属性节点(Attr 实例),并返回它。
var attribute = document.createAttribute(name);
document.createAttribute
方法的参数 name,是属性的名称。
var node = document.getElementById('div1');
var a = document.createAttribute('my_attrib');
a.value = 'newVal';
node.setAttributeNode(a);
// 或者
node.setAttribute('my_attrib', 'newVal');
上面代码为 div1
节点,插入一个值为 newVal
的 my_attrib
属性。
document.createComment()
document.createComment
方法生成一个新的注释节点,并返回该节点。
var CommentNode = document.createComment(data);
document.createComment
方法的参数是一个字符串,会成为注释节点的内容。
document.createDocumentFragment()
document.createDocumentFragment
方法生成一个空的文档片段对象(DocumentFragment 实例)。
var docFragment = document.createDocumentFragment();
DocumentFragment 是一个存在于内存的 DOM 片段,不属于当前文档,常常用来生成一段较复杂的 DOM 结构,然后再插入当前文档。这样做的好处在于,因为 DocumentFragment 不属于当前文档,对它的任何改动,都不会引发网页的重新渲染,比直接修改当前文档的 DOM 有更好的性能表现。
var docfrag = document.createDocumentFragment();
[1, 2, 3, 4].forEach(function (e) {
var li = document.createElement('li');
li.textContent = e;
docfrag.appendChild(li);
});
var element = document.getElementById('ul');
element.appendChild(docfrag);
上面代码中,文档片断 docfrag
包含四个 <li>
节点,这些子节点被一次性插入了当前文档。
删除节点
Node.removeChild()
removeChild
方法接受一个子节点作为参数,用于从当前节点移除该子节点。返回值是移除的子节点。
var divA = document.getElementById('A');
divA.parentNode.removeChild(divA);