Home > Article > Web Front-end > What you don’t know about the detailed knowledge of javascript operating DOM (1)_javascript skills
1: Node type
DOM level 1 defines a Node interface, which is implemented by all node types in the DOM. Each node has a nodeType attribute, which is used to indicate the type of the node. The node types include the following types in the Node type:
Node.ELEMENT_NODE(1); Element node
Node.ATTRIBUTE_NODE(2); Attribute node
Node.TEXT_NODE(3); Text node
Node.DOCUMENT_NODE(9); Document Node
In fact, there are many more types, but those are not very commonly used, so just understand these 4 types. Let’s take a look at the node types first, such as the following code:
The HTML code is as follows:
<div id="test"> <p>aaaaaa</p> <p>bbbbbb</p> <p>cccccc</p> </div>
JS is as follows:
var test = document.getElementById("test"); if(test.nodeType === Node.ELEMENT_NODE) { alert(1) }
The above code will not work in IE8 and below, and an error will be reported, as follows:
Because IE does not expose the constructor of the Node type, there will be errors under IE8-, but we can compare by numerical values. For example, if we want to compare element nodes above, we can use 1 to compare, and the same is true for attribute nodes. is 2, and the text node is 3; the following code:
var test = document.getElementById("test"); // 下面的所有的浏览器都支持 if(test.nodeType == 1) { alert(1) }
Understanding nodeName and nodeValue
nodeName saves the tag name of the element, and nodeValue is usually null; we can see the following code, there is no special explanation, the HTML code is all above, so the code will not be posted here; the following JS code test :
var test = document.getElementById("test"); if(test.nodeType == 1) { console.log(test.nodeName); // 打印DIV console.log(test.nodeValue); // 打印null }
Understand node relationships
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> </body> </html>
With the above code, we can consider head and body as child elements of html, and similarly html is their parent element. Then head and body are sibling elements, and then the elements inside head and body are child elements. We need What you understand is that each node has a childNodes attribute, which stores array-like elements. It also has a length attribute, but it is not an instance of the array Array. For example, we can look at the following test code:
<div id="test"> <p>aaaaaa</p> <p>bbbbbb</p> <p>cccccc</p> </div>
The JS code is as follows:
var test = document.getElementById("test"); if(test.nodeType == 1) { console.log(test.childNodes); console.log(test.childNodes.length); }
The above code, under standard browsers and IE9, the first line prints as follows:
[text, p, text, p, text, p, text, item: function]
The second line prints 7, the length is 7, because they also include the space of the text node, but in IE8 and below, the length is 3, they do not include the node of the text space, so if we want to unify, we can Write HTML code to remove spaces, as shown below;
024ec8e648c1f06980a2d32e6dcf0f0be388a4556c0f65e1904146cc1a846beeaaaaaa94b3e26ee717c64999d7867364b1b4a3e388a4556c0f65e1904146cc1a846beebbbbbb94b3e26ee717c64999d7867364b1b4a3e388a4556c0f65e1904146cc1a846beecccccc94b3e26ee717c64999d7867364b1b4a316b28748ea4df4d9c2150843fecfba68
This issue will be considered carefully later. Let’s take a look at how to get the child elements now. We can use 2 methods. The first is to use brackets [index] index, and the second is to use item [index] index. , the following code:
console.log(test.childNodes[1]); // e388a4556c0f65e1904146cc1a846beebbbbbb94b3e26ee717c64999d7867364b1b4a3
console.log(test.childNodes.item(1)); // e388a4556c0f65e1904146cc1a846beebbbbbb94b3e26ee717c64999d7867364b1b4a3
But they are not arrays. We can test the code, as follows:
console.log(Object.prototype.toString.call(test.childNodes) === "[object Array]");
// false but we convert it to an array, as shown in the following code:
//Invalid in IE8 and earlier versions
var arrayOfNodes = Array.prototype.slice.call(test.childNodes,0);
console.log(arrayOfNodes instanceof Array);
// true does not take effect in IE8 and earlier; because IE8 and earlier versions implement NodeList as a COM object, and we cannot use objects like JScript objects, we need to convert them to Array in lower versions of IE. In the form, we can encapsulate a method as follows;
function convertToArray(nodes){ var array = null; try { array = Array.prototype.slice.call(nodes, 0); //针对非IE 浏览器 } catch (ex) { array = new Array(); for (var i=0, len=nodes.length; i < len; i++){ array.push(nodes[i]); } } return array; } var test = document.getElementById("test"); var testArray = convertToArray(test.childNodes); console.log(testArray instanceof Array); // true
Understand parentNode (parent node), previousSibling (previous sibling node), nextSibling (next sibling node);
Each node has a parentNode attribute, which points to the parent node in the document. previousSibling refers to the previous sibling node of the current node, and nextSibling refers to the next sibling node of the current node. For example, the following code:
<div id="test"><p>aaaaaa</p><p>bbbbbb</p><p>cccccc</p></div> var test = document.getElementById("test"); if(test.nodeType == 1) { var secodeChild = test.childNodes[1]; console.log(secodeChild); // <p>bbbbbb</p> console.log(secodeChild.previousSibling); // <p>aaaaaa</p> console.log(secodeChild.nextSibling); // <p>cccccc</p> }
If there is only one node in the node list, then the previousSibling and nextSibling of the node are null; the firstChild of the parent node points to the first node in the parent node; the following code:
024ec8e648c1f06980a2d32e6dcf0f0b25d745d5ee28dbdbed8fe24a8ecaf70baaaaaa94b3e26ee717c64999d7867364b1b4a3a9add295b633290750524bdb3798830fbbbbbb94b3e26ee717c64999d7867364b1b4a327e4fa36f65cd23bf5b2f30c5e49463bcccccc94b3e26ee717c64999d7867364b1b4a316b28748ea4df4d9c2150843fecfba68
JS is as follows:
var test = document.getElementById("test"); if(test.nodeType == 1) { console.log(test.firstChild); // <p class="a">aaaaaa</p> console.log(test.lastChild); // <p class="c">cccccc</p> }
The firstChild of the parent node is always equal to the .childNodes[0] of the parent node, and the lastChild of the parent node is always equal to the .childNodes[.childNodes.length of the parent node - 1]; The following code:
console.log(test.firstChild === test.childNodes[0]); // true console.log(test.lastChild === test.childNodes[test.childNodes.length - 1]); // true
如果没有子节点的话,那么firstChild和lastChild都指向为空null;
hasChildNodes():如果需要判断该父节点有没有子节点的话,可以使用该方法判断,返回的是一个布尔型,有返回true,没有返回false,如下代码:
024ec8e648c1f06980a2d32e6dcf0f0b25d745d5ee28dbdbed8fe24a8ecaf70baaaaaa94b3e26ee717c64999d7867364b1b4a3a9add295b633290750524bdb3798830fbbbbbb94b3e26ee717c64999d7867364b1b4a344e5613e3a3cb436aa54a0c1401ecf2dcccccc94b3e26ee717c64999d7867364b1b4a316b28748ea4df4d9c2150843fecfba68
JS代码如下:
var test = document.getElementById("test");
console.log(test.hasChildNodes());
// true如果是如下的 就返回false;如下代码:
024ec8e648c1f06980a2d32e6dcf0f0b16b28748ea4df4d9c2150843fecfba68
var test = document.getElementById("test");
console.log(test.hasChildNodes()); // false
ownerDocument: 所有节点都有最后一个属性是ownerDocument,该属性指向表示整个文档的文档节点,这种关系表示的任何节点都属于它所在的文档,任何节点都不能同时存在两个或更多文档中,通过这个属性,我们可以不必在节点层次中通过层层回溯到达顶端,而是可以直接访问文档节点;如下测试代码:
<div id="test"> <p class="a">11</p> </div> var test = document.getElementById("test"); console.log(test.ownerDocument); // document var p = test.ownerDocument.getElementsByTagName("p"); console.log(p); // <p class="a">11</p>
appendChild(): 用于向childNodes列表的末尾添加一个节点;返回的是新增加的节点;如下代码:
024ec8e648c1f06980a2d32e6dcf0f0b
25d745d5ee28dbdbed8fe24a8ecaf70b1194b3e26ee717c64999d7867364b1b4a3
16b28748ea4df4d9c2150843fecfba68
JS代码如下:
var test = document.getElementById("test"); var newNode = document.createElement("p"); var returnNode = test.appendChild(newNode); console.log(returnNode); // <p></p> console.log(returnNode === newNode); // true console.log(test.lastChild === newNode); // true
insertBefore(): 该方法是将新节点插入到指定的节点的前面去,该方法接收2个参数,要插入的节点和作为参照的节点;插入节点后,被插入的节点会变成参照节点的前一个同胞节点,同时被方法返回,如下代码:
024ec8e648c1f06980a2d32e6dcf0f0b
25d745d5ee28dbdbed8fe24a8ecaf70b1194b3e26ee717c64999d7867364b1b4a3
16b28748ea4df4d9c2150843fecfba68
JS代码如下:
var test = document.getElementById("test"); var newNode = document.createElement("div"); var returnNode = test.insertBefore(newNode,test.childNodes[0]); console.log(returnNode); // <div></div> console.log(returnNode === newNode); // true
插入节点后,结构变成如下:
但是如果参照节点为null的话,那么就会把新节点插入到最后面去了,如下代码:
我们还可以更深入的看下如下测试代码:
var test = document.getElementById("test"); var newNode = document.createElement("div"); // 插入后成为最后一个节点 var returnNode = test.insertBefore(newNode,null); console.log(returnNode === test.lastChild); // true // 插入后成为第一个节点 var returnNode = test.insertBefore(newNode,test.firstChild); console.log(returnNode === newNode); // true console.log(newNode === test.firstChild); // true // 插入到最后一个子节点的前面 var returnNode = test.insertBefore(newNode,test.lastChild); console.log(returnNode === test.childNodes[test.childNodes.length - 2]); // true
replaceChild(); 该方法接收2个参数,要插入的节点和要替换的节点,要替换的节点将由这个方法返回并从文档树中被移除,同时由插入的节点占据其位置,如下代码:
024ec8e648c1f06980a2d32e6dcf0f0b25d745d5ee28dbdbed8fe24a8ecaf70b1194b3e26ee717c64999d7867364b1b4a3a9add295b633290750524bdb3798830f2294b3e26ee717c64999d7867364b1b4a316b28748ea4df4d9c2150843fecfba68
JS代码如下:
var test = document.getElementById("test"); var newNode = document.createElement("div"); // 替换第一个节点 var returnNode = test.replaceChild(newNode,test.firstChild); console.log(returnNode); // <p class="a">11</p>
替换后html代码结构变为如下:
024ec8e648c1f06980a2d32e6dcf0f0bdc6dce4a544fdca2df29d5ac0ea9906b16b28748ea4df4d9c2150843fecfba68a9add295b633290750524bdb3798830f2294b3e26ee717c64999d7867364b1b4a316b28748ea4df4d9c2150843fecfba68
替换最后一个节点代码如下:
var test = document.getElementById("test"); var newNode = document.createElement("div"); // 替换最后一个节点 var returnNode = test.replaceChild(newNode,test.lastChild); console.log(returnNode); // <p class="b">22</p>
替换后的代码如下:
024ec8e648c1f06980a2d32e6dcf0f0b25d745d5ee28dbdbed8fe24a8ecaf70b1194b3e26ee717c64999d7867364b1b4a3dc6dce4a544fdca2df29d5ac0ea9906b16b28748ea4df4d9c2150843fecfba6816b28748ea4df4d9c2150843fecfba68removeChild():移除节点,该方法接收一个参数,即要移除的节点;
被移除的节点将成为返回值,如下代码:
var test = document.getElementById("test"); var newNode = document.createElement("div"); // 移除第一个节点 var returnNode = test.removeChild(test.firstChild); console.log(returnNode); // <p class="a">11</p>
移除后的代码结构变为如下:
024ec8e648c1f06980a2d32e6dcf0f0ba9add295b633290750524bdb3798830f2294b3e26ee717c64999d7867364b1b4a316b28748ea4df4d9c2150843fecfba68
移除最后一个节点的代码如下:
// 移除最后一个节点
var returnNode = test.removeChild(test.lastChild);
console.log(returnNode); // a9add295b633290750524bdb3798830f2294b3e26ee717c64999d7867364b1b4a3
移除后的代码结构变为如下:
024ec8e648c1f06980a2d32e6dcf0f0b25d745d5ee28dbdbed8fe24a8ecaf70b1194b3e26ee717c64999d7867364b1b4a316b28748ea4df4d9c2150843fecfba68cloneNode():
克隆一个相同的副本,该方法接收一个布尔值参数,如果为true的话,说明是深复制,复制该节点及整个子节点书,如果为false的话,只复制该节点本身,比如如下代码:
var test = document.getElementById("test"); var deeplist = test.cloneNode(true); console.log(deeplist); // <div id="test"><p class="a">11</p><p class="b">22</p></div> 如果是浅复制的如下代码: var test = document.getElementById("test"); var deeplist = test.cloneNode(false); console.log(deeplist);// <div id="test"></div>
Document类型
JS通过Document类型表示文档,document继承HTMLDocument(继承自Document)类型的一个实例,表示整个HTML页面。document对象是window的一个属性,所以可以通过全局对象来访问,document对象有如下特点:
nodeType值为9,
nodeName值为”#document”,
nodeValue值为null,
parentNode值为null,
ownerDocument值为null
理解文档document的子节点
documentElement: 该属性始终指向html元素,如下代码:
console.log(document.documentElement); // 指向html的引用
如果需要直接访问html元素的引用可以直接使用该元素更快,更方便。
childNodes: 通过childNodes列表访问文档元素;如下测试代码:
console.log(document.documentElement); // 指向html的引用 console.log(document.childNodes[0]); // <!doctype html> console.log(document.firstChild); // <!doctype html> console.log(document.firstChild === document.childNodes[0]); // true console.log(document.body); // 指向body的引用
所有的浏览器都支持document.documentElement 和 document.body属性;
title: 通过这个属性可以取得网页的标题,如下测试代码:
console.log(document.title);
也可以该方法设置网页的标题;如下代码:
document.title = “我是龙恩”;
URL: 取得页面中完整的url,如下代码测试:
console.log(document.URL); // http://127.0.0.1/dom/dom.html
domain: 该属性包含页面的域名;如下测试代码:
console.log(document.domain); // 127.0.0.1
域名也可以设置的,比如常见的跨子域的情况下,我们需要设置相同的父域即可完成跨域;
namedItem(): 该方法可以通过元素的name特性取得集合中的项,比如现在页面上有很多input的框,我想通过他们的name属性取得自己想要的哪一项,测试代码如下:
HTML代码如下:
a6868d61bc83ef1a96de101853a57ffb
b63909539cf4b5655763ff831743ea2a
JS代码如下:
var inputs = document.getElementsByTagName("input");
console.log(inputs.namedItem("aa")); // a6868d61bc83ef1a96de101853a57ffb
我们还可以通过方括号的语法来访问,如下代码:
console.log(inputs["aa"]); // a6868d61bc83ef1a96de101853a57ffb
要想取得文档中所有的元素,可以向getElementsByTagName()中传入”*”; 星号表示全部的意思;
getElementsByName(); 该方法也是HTMLDocument类型才有的方法,这个方法会返回带有给定name特性的所有元素,如下测试代码:
<input name="aa" type="radio"/> <input name="aa" type="radio"/> var aa = document.getElementsByName("aa"); console.log(aa); // object console.log(aa.length); // 2
该方法也会返回一个HTMLCollection.
浏览器支持程度IE,firefox,chrome都支持;
Element类型
Element节点有以下特征:
nodeType的值为1;
nodeName的值为元素的标签名;
nodeValue的值为null;
parentNode可能是Document或Element
要访问元素的标签名,可以使用nodeName属性,也可以使用tagName属性,这两个属性会返回相同的值,如下代码:
<div id="test"></div> var test = document.getElementById("test"); console.log(test.tagName); // DIV console.log(test.nodeName); // DIV console.log(test.nodeValue); // null; console.log(test.parentNode); // body console.log(test.nodeType); // 1
理解HTML元素中的获取属性的方法
所有HTML元素都由HTMLElement类型表示,HTMLElement类型直接继承Element;如果想要取得HTML元素的特性的话,有下面几个方法:
getAttribute(); 获取元素的属性的方法;如下测试代码:
<div id="test" class="testName" title="aa"></div> var test = document.getElementById("test"); console.log(test.getAttribute("id")); // test console.log(test.getAttribute("class")); // testName console.log(test.getAttribute("title")); // aa
注意:上面的获取类名在IE8+及标准浏览器下都是使用获取属性class来取得类名,上面的如果在IE7下会返回null, 但是在IE7及以下是使用className这个属性来获取的;如下代码:在IE7下测试:
console.log(test.getAttribute("className")); // testName
当然也可以取得自定义的属性;如下代码:
a8743f7f728a156d8fb9ae7f2f5faf3b16b28748ea4df4d9c2150843fecfba68
console.log(test.getAttribute("data-value")); // test1
setAttribute() 设置元素的属性;该方法接收2个参数,第一个参数是属性名称,第二个参数是对应的值;如果该值存在,则会以现在的值替换之前的值,如果属性名不存在,则会创建该属性,并指定该属性的值;如下代码:
<div id="test"></div> // JS代码如下: var test = document.getElementById("test"); test.setAttribute("id",'test'); test.setAttribute("class","testName"); test.setAttribute("title","aa"); test.setAttribute("data-value","test1");
生成HTML结构如下:
79f3e23204a3e53626bc9850091dd65316b28748ea4df4d9c2150843fecfba68
removeAttribute(); 删除元素的特性;如下代码:
79f3e23204a3e53626bc9850091dd65316b28748ea4df4d9c2150843fecfba68
JS代码如下:
var test = document.getElementById("test"); test.removeAttribute("id"); test.removeAttribute("class"); test.removeAttribute("title"); test.removeAttribute("data-value");
IE7及以下删除类名需要使用className;
attributes属性
Element类型是使用attributes属性的唯一一个dom节点类型,attributes属性包含一个NamedNodeMap集合;该对象有以下方法:
getNamedItem(name): 返回nodeName属性等于name的节点;
attributes属性中包含一系列节点,每个节点的nodeName就是特性的名称,而节点的nodeValue就是节点值,要取得元素id的特性,如下代码:
79f3e23204a3e53626bc9850091dd65316b28748ea4df4d9c2150843fecfba68JS代码如下:
我们也可以通过中括号的语法来获取的,如下代码:
同样我们也可以通过上面介绍的getAttribute(“id”)方法来获取元素,如下代码:
console.log(div.getAttribute("id")); // test
也可以通过以下方法给元素设置id,如下方法:
div.attributes["id"].nodeValue = "aa";
removeNamedItem(name): 从列表中移除nodeName属性等于name的节点;
调用removeNamedItem()方法与在元素上调用removeAttribute()方法的效果相同,是直接删除具有给定名称的特性。他们之间的区别是:removeNamedItem()方法返回表示被删除特性的节点;
如下代码:
79f3e23204a3e53626bc9850091dd65316b28748ea4df4d9c2150843fecfba68
JS代码如下:
现在的html结构变成如下:
2ea8b42782e6b08cfccf5d6ce8200d2b16b28748ea4df4d9c2150843fecfba68
如上方法:目前IE,firefox,chrome浏览器都支持;
但是上面我们有getAttribute,setAttribute(),removeAttribute()方法,我们可以使用这些方法足够代替上面介绍的几个方法,但是如果想遍历一个元素上有多少个属性时,attributes这个就可以派上用上了,如下代码:
HTML代码如下:
79f3e23204a3e53626bc9850091dd65316b28748ea4df4d9c2150843fecfba68
JS代码如下:
var div = document.getElementById("test"); function outputAttributes(element) { var arrs = new Array(); var attrName, attrValue, i, len; for(i = 0, len = element.attributes.length; i < len; i++) { attrName = element.attributes[i].nodeName; attrValue = element.attributes[i].nodeValue; arrs.push(attrName + "='" + attrValue + "'"); } return arrs.join(" "); } console.log(outputAttributes(div));
结果打印如下:
id='test' class='testName' title='aa' data-value='test1'
上面的函数使用一个数组来保存名值对,最后再以空格为分隔符将它们拼接起来,但是上面的代码我们需要注意两点:
1.针对attributes对象中的特性,不同的浏览器返回的顺序不同。
2.IE7及更早的版本会返回HTML元素中所有可能的特性,包括没有指定的特性;
针对IE7的问题我们可以进行改进,我们都知道,每个特性节点都有一个名为specified的属性,这个属性值如果为true,则意味着要么在HTML中指定了相应的特性,要么通过setAttribute方法设置了该特性,在IE中,所有为设置过的特性该属性的之都为false;如下代码:
var div = document.getElementById("test"); function outputAttributes(element) { var arrs = new Array(); var attrName, attrValue, i, len; for(i = 0, len = element.attributes.length; i < len; i++) { attrName = element.attributes[i].nodeName; attrValue = element.attributes[i].nodeValue; if(element.attributes[i].specified) { arrs.push(attrName + "='" + attrValue + "'"); } } return arrs.join(" "); } console.log(outputAttributes(div));
理解元素的子节点
元素可以有任意数目的子节点和后代节点,元素的childNodes属性中它包含了它的所有子节点,这些子节点可能是元素,文本节点,注释或处理指令;不同的浏览器子节点的个数不一样,下面我们来看看demo如下:
<ul id="list"> <li>11</li> <li>22</li> <li>33</li> </ul>
JS代码如下:
我们之前在上面讲过,在标准浏览器下(IE9+,firefox,chrome)下,打印的长度是7个,因为他们把空格也包含上去,在IE8-及以下的浏览器返回的是3个子元素,这是表现的正常情况下,但是如果我现在把html元素的空格都去掉的话,那么所有的浏览器都会返回3个子元素,如下代码:
51c0c1ec9a4a59777756ffc2ebbc51ab25edfb22a4f469ecb59f1190150159c611bed06894275b65c1ab86501b08a632eb25edfb22a4f469ecb59f1190150159c622bed06894275b65c1ab86501b08a632eb25edfb22a4f469ecb59f1190150159c633bed06894275b65c1ab86501b08a632eb929d1f5ca49e04fdcb27f9465b944689
JS代码如下:
那么平时我们不可能都这样小心小意的编写html代码的,因为一步小心就有一个空格产生,因此我们可以如下判断下是不是元素节点;如下代码演示:
<ul id="list"> <li>11</li> <li>22</li> <li>33</li> </ul>
JS代码如下:
var list = document.getElementById("list"); var arrs = []; for(var i = 0, ilen = list.childNodes.length; i < ilen; i++) { var curElement = list.childNodes[i]; if(curElement.nodeType === 1){ arrs.push(curElement); } } console.log(arrs); console.log(arrs.length); // 3
如上代码在所有的浏览器都支持,通过判断该元素是不是元素节点,然后在执行后续操作;不是元素节点都会过滤掉,因此返回的长度都为3;
理解Text类型
Text节点具有以下特征:
nodeType的值为3;
nodeName的值为”#text”;
nodeValue的值为节点所包含的文本;
parentNode是一个Element;
没有子节点;
1.创建文本节点;
可以使用document.createTextNode()创建新文本节点,这个方法需要一个参数,即要插入的文本,如下代码:
var element = document.createElement("div"); var text = document.createTextNode("aa"); element.appendChild(text); document.body.appendChild(element);
2. 分割文本节点splitText(offset); 从offset指定的位置将当前的文本节点分成2个文本节点;如下代码:
var element = document.createElement("div"); var text = document.createTextNode("aabbbccc"); element.appendChild(text); document.body.appendChild(element); var newNode = element.firstChild.splitText(5); console.log(element.firstChild.nodeValue); // aabbb console.log(newNode.nodeValue); // ccc console.log(element.childNodes.length); // 2
浏览器支持的程度有IE,firefox,chrome都支持;
理解DOM操作—动态插入脚本
我们动态插入JS脚本,常见的写法有如下代码即可插入:
var script = document.createElement("script"); script.type ="text/javascript"; script.src = 'a.js'; document.body.appendChild(script);
即可在body最后面动态插如a.js,此动态插入不会堵塞浏览器加载;我们为了更加扩张性,我们也可以封装一个函数,如下代码:
function loadScript(url) { var script = document.createElement("script"); script.type = "text/javascript"; script.src = url; document.body.appendChild(script); } loadScript("a.js");
当页面有多个js需要动态插入的话,可以依次调用loadScript函数即可;
但是假如我们现在有这么一个需求,动态加载JS完后,我想在这后面做点事情,我们都知道,动态加载也可以理解为异步加载,不会堵塞浏览器,但是我们需要如何知道动态JS已经加载完了呢?
我们之前讲过一篇文章是 “Javascript事件总结”中有判断JS动态加载完后使用load事件来监听js是否动态加载完,现在我们再来复习下load事件;
Javascript中最常用的一个事件是load,当页面加载完后(包括所有的图片,javascript文件,css文件等外部资源)。就会触发window上面的load事件;目前可以有2种触发load事件,一种是直接放在body上面触发;比如如下代码:
e80d372592ed24cc5e09dbe63961d82d36cc49f0c466276486e50c850b7e4956
第二种是通过JS来触发,如下代码:
上面的EventUtil.addHandler 是我们之前在javascript事件总结中封装的JS函数,现在我们直接拿过来使用,如下代码:
var EventUtil = { addHandler: function(element,type,handler) { if(element.addEventListener) { element.addEventListener(type,handler,false); }else if(element.attachEvent) { element.attachEvent("on"+type,handler); }else { element["on" +type] = handler; } }, removeHandler: function(element,type,handler){ if(element.removeEventListener) { element.removeEventListener(type,handler,false); }else if(element.detachEvent) { element.detachEvent("on"+type,handler); }else { element["on" +type] = null; } }, getEvent: function(event) { return event ? event : window.event; }, getTarget: function(event) { return event.target || event.srcElement; }, preventDefault: function(event){ if(event.preventDefault) { event.preventDefault(); }else { event.returnValue = false; } }, stopPropagation: function(event) { if(event.stopPropagation) { event.stopPropagation(); }else { event.cancelBubble = true; } } };
下面我们来看看如何使用JS事件来判断JS是否加载完成的代码,我们可以使用load事件来监听,如下代码:
EventUtil.addHandler(window,'load',function(){ var script = document.createElement("script"); EventUtil.addHandler(script,'load',function(event){ console.log(script.src); }); script.src = 'a.js'; document.body.appendChild(script); });
如上代码,script元素也会触发load事件,以便可以确定动态加载jS是否加载完成,上面的代码指定src属性和指定的事件处理程序的事件可以调换位置,先后顺序不重要,如上代码,我们也可以这样写,如下代码:
EventUtil.addHandler(window,'load',function(){ var script = document.createElement("script"); script.src = 'a.js'; document.body.appendChild(script); EventUtil.addHandler(script,'load',function(event){ console.log(script.src); }); });
目前只有IE9+,Firefox,opera,chrome和safari3+支持,IE8及以下不支持该load事件,因此我们需要针对IE8及以下做处理;
理解readystatechange事件
IE为DOM文档中的某些部分提供了readystatechange事件,这个事件的目的提供与文档或元素的加载的状态有关信息,此事件提供了下面5个值中的一个;
uninitialized(未初始化):对象存在但尚未初始化;
loading(正在加载):对象正在加载数据;
loaded(加载完毕): 对象加载数据完成;
interactive(交互): 可以操作对象了,但没有完全加载;
complete(完成):对象已经加载完成;
3f1c4e4b6b16bbbd69b2ee476dc4f83a(在IE和opera)中和2cdf5bf648cf2f33323966d7f58a7f3f(仅IE中)元素也会触发readystatechange事件,可以用来确定外部的javascript和css文件是否已经加载完成,基于元素触发的readystatechange事件,即readyState属性无论等于”loaded”还是”complete”都可以表示资源已经可用;如下针对IE的判断javascript是否已经加载完成的方案:
EventUtil.addHandler(window,'load',function(){ var script = document.createElement("script"); EventUtil.addHandler(script,'readystatechange',function(event){ event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); if (target.readyState == "loaded" || target.readyState == "complete"){ EventUtil.removeHandler(target, "readystatechange", arguments. callee); alert(script.src); } }); script.src = 'a.js'; document.body.appendChild(script); });
下面我们可以使用javascript客户端检测技术来判断是否是IE和其他标准浏览器做一个简单的封装;如下代码:
var ua = navigator.userAgent.toLowerCase(); EventUtil.addHandler(window,'load',function(){ var script = document.createElement("script"); if(/msie ([^;]+)/.test(ua) || "ActiveXObject" in window) { // IE 浏览器 EventUtil.addHandler(script,'readystatechange',function(event){ event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); if (target.readyState == "loaded" || target.readyState == "complete"){ EventUtil.removeHandler(target, "readystatechange", arguments. callee); console.log("javascript已经加载完成"); } }); }else { // 除IE之外的标准浏览器 EventUtil.addHandler(script,'load',function(event){ console.log("javascript已经加载完成"); }); } script.src = 'a.js'; document.body.appendChild(script); });
上面的是针对所有的主要的浏览器进行封装来判断动态加载的JS是否已经加载完成!
理解动态加载行内JS方式
另一种加载javascript代码的方式是行内方式,如下代码:
从理论来讲,下面的代码应该是有效的;
如上代码在firefox,IE9+,chrome和opera中,都是正常的,可以运行的,但是在IE8及以下会报错,如下:
IE8及以下将script视为一个特殊的元素,不允许dom访问其子节点;但是我们可以使用script元素的text属性来指定javascript代码,如下代码:
var script = document.createElement('script'); script.type = 'text/javascript'; script.text = "function sayHi(){alert(1);} sayHi()"; document.body.appendChild(script);
现在在所有的主流的浏览器都是正常的,在IE8及以下也是正常的;但是在safari3.0之前的版本不能正确的支持text属性,但可以允许使用文本节点来指定代码;如下代码:
var script = document.createElement('script'); script.type = 'text/javascript'; var code = "function sayHi(){alert(1);} sayHi()"; try{ script.appendChild(document.createTextNode(code)); }catch(e){ script.text = code; } document.body.appendChild(script);
下面我们可以使用一个函数来封装下;如下代码:
function loadScriptString(code) { var script = document.createElement('script'); script.type = 'text/javascript'; try{ script.appendChild(document.createTextNode(code)); }catch(e){ script.text = code; } document.body.appendChild(script); } loadScriptString("function sayHi(){alert(1);} sayHi()");
理解动态加载css样式
与动态脚本类似,所谓动态样式是指在页面刚载入时不存在的样式,动态样式是在页面加载完成后动态添加到页面中的;
动态加载样式如下代码:
var link = document.createElement("link"); link.rel = "stylesheet"; link.type = "text/css"; link.href = "style.css"; var head = document.getElementsByTagName("head")[0]; head.appendChild(link);
如上代码即可动态生成css样式;
我们也可以像JS一样先封装一个函数,为了调用更加方便;如下代码:
function loadStyles(url) { var link = document.createElement("link"); link.rel = "stylesheet"; link.type = "text/css"; link.href = url; var head = document.getElementsByTagName("head")[0]; head.appendChild(link); } loadStyles("style.css");
我们也可以使用事件的方式来判断css动态加载是否已经加载完成~
EventUtil.addHandler(window,'load',function(){ var link = document.createElement("link"); link.rel = "stylesheet"; link.type = "text/css"; EventUtil.addHandler(link, "load", function(event){ console.log("css loaded"); }); link.href = "style.css"; document.getElementsByTagName("head")[0].appendChild(link); });
注意:如上代码:不仅在标准浏览器下chrome,firefox,opera下支持,而且IE7-10都支持这种加载;但是safari不支持这种加载;
或者和javascript一样,我们也可以针对IE下做一种这样的处理;代码如下,也可以支持所有的IE下:
EventUtil.addHandler(window, "load", function(){ var link = document.createElement("link"); link.type = "text/css"; link.rel= "stylesheet"; EventUtil.addHandler(link, "readystatechange", function(event){ event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); if (target.readyState == "loaded" || target.readyState == "complete"){ EventUtil.removeHandler(target, "readystatechange", arguments. callee); alert("CSS Loaded"); } }); link.href = "style.css"; document.getElementsByTagName("head")[0].appendChild(link); });
我们也可以像Javascript一样,为了判断所有的浏览器下支持可以封装一个所有都支持的代码;当然除safari浏览器除外,好像safari浏览器没有生效;
如下代码:
var ua = navigator.userAgent.toLowerCase(); EventUtil.addHandler(window,'load',function(){ var link = document.createElement("link"); link.type = "text/css"; link.rel= "stylesheet"; if(/msie ([^;]+)/.test(ua) || "ActiveXObject" in window) { // IE 浏览器 EventUtil.addHandler(link,'readystatechange',function(event){ event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); if (target.readyState == "loaded" || target.readyState == "complete"){ EventUtil.removeHandler(target, "readystatechange", arguments. callee); alert("css已经加载完成"); } }); }else { // 除IE之外的标准浏览器 EventUtil.addHandler(link,'load',function(event){ alert("css已经加载完成"); }); } link.href = "style.css"; document.getElementsByTagName("head")[0].appendChild(link); });
另一种定义样式的方式是使用c9ccee2e6ea535a969eb3f532ad9fe89元素来包含嵌入式的css
如下所示代码:
c9ccee2e6ea535a969eb3f532ad9fe89
body {background-color:red;}
531ac245ce3e4fe3d50054a55f265927
按照相同的原理,下面的代码除了IE8及以下不支持,其他浏览器下都支持;如下代码:
var style = document.createElement("style"); style.type = "text/css"; style.appendChild(document.createTextNode("body{background-color:red}")); var head = document.getElementsByTagName("head")[0]; head.appendChild(style);
因此我们也可以像JS一样封装一个全兼容的css样式;如下代码所示:
var style = document.createElement("style"); style.type = "text/css"; try{ style.appendChild(document.createTextNode("body{background-color:red}")); } catch (ex){ style.styleSheet.cssText = "body{background-color:red}"; } var head = document.getElementsByTagName("head")[0]; head.appendChild(style);
为了更方便调用,因此我们也可以封装一个函数来;如下代码:
function loadStyleString(css) { var style = document.createElement("style"); style.type = "text/css"; try{ style.appendChild(document.createTextNode(css)); } catch (ex){ style.styleSheet.cssText = css; } var head = document.getElementsByTagName("head")[0]; head.appendChild(style); } // 调用方式如下: loadStyleString("body{background-color:red}");
DOM的扩展
1. querySelector()方法
querySelector()方法接收一个css选择符,返回与该模式匹配的第一个元素,如果没有找到匹配元素,则返回null;如下代码:
JS代码如下:
// 取得body var body = document.querySelector('body'); console.log(body); // body的引用 // 取得id为test的元素 var test = document.querySelector("#test"); console.log(test); // <div id="test">test</div> // 取得类为cls的第一个元素 var cls = document.querySelector(".cls"); console.log(cls); // <div class="cls">1111</div>
同理我们也可以通过Element类型调用querySelector()方法时,只会在该元素后代范围内查找匹配的元素,如下代码:
我想通过先取得div中的testParent,然后再取得p元素(通过querySelector方法);如下代码:
var testParent = document.querySelector('.testParent'); var pfirst = testParent.querySelector("p"); console.log(pfirst); // <p>1111</p>
浏览器支持程度:IE8+,firefox,chrome,safari3.1+,opera10+
2. querySelectorAll()方法
querySelectorAll()方法接收的参数与querySelector()方法一样,都是一个css选择符,但是返回的是所有匹配的元素,这个方法返回的是一个NodeList的实例。
如下代码演示:
<div id="test"> <em class="selected">11111</em> <em class="selected">22222</em> <em>33333</em> <em>44444</em> </div>
JS代码如下:
// 取得div中所有em元素 var em = document.getElementById("test").querySelectorAll('em'); console.log(em); // NodeList console.log(em.length); // 4 // 第二种方式 也可以先使用querySelector var test = document.querySelector("#test"); var em2 = test.querySelectorAll('em'); console.log(em2); // NodeList console.log(em2.length); // 4 // 取得类为selected的所有元素 var selected = document.querySelectorAll('.selected'); console.log(selected); // <em class="selected">11111</em> // <em class="selected">22222</em> // 也可以通过以下的方式获得所有的em元素 var em = document.querySelectorAll('div em'); console.log(em); // NodeList console.log(em.length); // 4
要取得返回的NodeList中的每一个元素,可以使用item()方法,也可以使用方括号的语法;
var ems = document.querySelectorAll('#test em'); for(var i = 0, ilen = ems.length; i < ilen; i++) { console.log(ems[i]); // 取得每一项em console.log(ems.item(i)); // 取得每一项em }
浏览器支持程度:IE8+,firefox,chrome,safari3.1+,opera10+
3. matchesSelector()方法
这个方法接收一个参数,即css选择符,如果调用元素与该选择符匹配,返回true,否则,返回false;
如下代码:
console.log(document.body.webkitMatchesSelector("body")); // true
IE9+通过msMatchesSelector()支持该方法,Firefox3.6+通过mozMatchesSelector()支持该方法;safari5+和chrome通过webkitMatchesSelector()支持该方法;因此我们可以包装一个函数,如下代码:
function matchesSelector(element,selector) { if(element.MatchesSelector) { return element.MatchesSelector(selector); }else if(element.msMatchesSelector) { return element.msMatchesSelector(selector); }else if(element.webkitMatchesSelector) { return element.webkitMatchesSelector(selector); }else if(element.mozMatchesSelector) { return element.mozMatchesSelector(selector); }else { throw new Error("Not supported"); } } console.log(matchesSelector(document.body,'body')); // true var test = document.querySelector('#test'); console.log(matchesSelector(test,'#test')); // true
4. getElementsByClassName() 方法
该方法接收一个参数,即包含一个或者多个类名的字符串,返回带有指定类的所有元素的NodeList;传入多个类名时,类名的先后顺序不重要。
console.log(document.getElementsByClassName("p1")); // 返回NodeList
支持getElementsByClassName()方法的浏览器有IE9+,firefox3+,safari3.1+,chrome和opera9.5+;
5. classList属性
如下代码:
3f5c08ea23be22d5fee4de628b14ac10aaa16b28748ea4df4d9c2150843fecfba68
我想删除aa这个类名的时候,以前需要如下写代码:
var div = document.getElementById("div"); // 首先,取得类名字符串并拆分成数组 var classNames = div.className.split(/\s+/); var pos = -1, i, len; for(i = 0,len = classNames.length; i < len; i+=1) { if(classNames[i] == 'aa') { pos = i; break; } } // 删除类名 classNames.splice(i,1); // 把剩下的类名拼成字符串并重新设置 div.className = classNames.join(" "); // <div class="bb cc" id="div">aaa</div>
但是现在HTML5新增了一种操作类名的方式,可以让操作更简单也更方便,那就是所有元素添加classList属性。这个classList属性是新集合类型DOMTokenList的实例;DOMTokenList有一个表示自己包含多少元素的length属性,而要取得每个元素可以使用item()方法,也可以使用中括号;
还有以下方法:
add(value):将给定的字符串值添加到列表中。如果值已经存在,就不添加了。
contains(value):表示列表中是否存在给定的值,如果存在则返回true,否则返回false。
remove(value):从列表中删除给定的字符串。
toggle(value):如果列表中已经存在给定的值,删除它;如果列表中没有给定的值,添加它。
现在我们可以来看看使用上面的方法的优点如下,我们可以来做个demo,代码如下:
3f5c08ea23be22d5fee4de628b14ac10aaa16b28748ea4df4d9c2150843fecfba68
JS如下:
var div = document.getElementById("div"); // 删除类名aa,如下方法即可 div.classList.remove("aa"); // html结构如下: <div class="bb cc" id="div">aaa</div> // 添加类名current,如下方法即可 div.classList.add("current"); // html结构变为如下 <div class="bb cc current" id="div">aaa</div> // 切换aa类名 div.classList.toggle("aa"); // html结构变为如下 <div class="aa bb cc current" id="div">aaa</div> // 确定元素是否包含类名; console.log(div.classList.contains("aa")); // true
支持classList属性的浏览器有IE10+,firefox3.6+和chrome
HTML5新增自定义数据属性
HTML5中规定可以为元素添加非标准的属性,但要添加前缀data-,比如如下代码:
0b484fa75419ccb73d6e721b7d149bcc16b28748ea4df4d9c2150843fecfba68
添加了自定义属性之后,可以通过元素的dataset属性来访问自定义属性的值,dataset属性的值是DOMStringMap的一个实例,也就是一个名值对的映射;比如如下代码:
//本例中使用的方法仅用于演示 var div = document.getElementById("myDiv"); console.log(div.dataset); // DOMStringMap {value: "12345", name: "Nicholas"} //取得自定义属性的值 var value1 = div.dataset.value; console.log(value1); // 12345 var name1 = div.dataset.name; console.log(name1); // Nicholas //设置值 div.dataset.value = 23456; div.dataset.name = "Michael"; //有没有"myname"值呢? if (div.dataset.name){ console.log("Hello, " + div.dataset.name); // Hello, Michael }
浏览器支持的程度有;firefox6+和chrome浏览器;