文章目录
一、dom加载完毕执行js
dom可以看成一个树形结构,dom的加载顺序就是从上到下,我们一再强调将js文件放在尾部,就是因为如果js脚本在dom文档构造之前执行,这样js就无法访问dom文档对象模型。
1.1 jquery
- 等待页面dom树加载完毕,再执行代码
写法一:标准写法
$(document).ready(function(){
...
});
写法二:简写
$(function(){
....
})
两种写法是等价的
- 等待页面所有资源(包括css样式表、dom树、图片、第三方框架)加载完毕,再执行代码
$(window).load(function(){
...
});
总结:也就是说$(function(){...})
会比$(window).load(function(){...})
先执行
1.2 js
- onload事件:等待页面所有资源(包括dom树、图片、第三方框架)加载完毕,再执行代码
window.onload = function(){
...
}
- DOMContentLoaded 事件:等待页面dom树加载完毕,再执行代码
jquery中使用的$(document).ready(function(){});
实际上监听的就是DOMContentLoaded
事件。
function domReady(fn){
//对于现代浏览器,对DOMContentLoaded事件的处理采用标准的事件绑定方式
if ( document.addEventListener ) {
document.addEventListener("DOMContentLoaded", fn, false);
} else {
IEContentLoaded(fn);
}
//IE模拟DOMContentLoaded
function IEContentLoaded (fn) {
var d = window.document;
var done = false;
//只执行一次用户的回调函数init()
var init = function () {
if (!done) {
done = true;
fn();
}
};
(function () {
try {
// DOM树未创建完之前调用doScroll会抛出错误
d.documentElement.doScroll('left');
} catch (e) {
//延迟再试一次~
setTimeout(arguments.callee, 50);
return;
}
// 没有错误就表示DOM树创建完毕,然后立马执行用户回调
init();
})();
//监听document的加载状态
d.onreadystatechange = function() {
// 如果用户是在domReady之后绑定的函数,就立马执行
if (d.readyState == 'complete') {
d.onreadystatechange = null;
init();
}
}
}
}
二、创建节点
-
普通创建节点
- 创建新的元素节点:document.createElement(‘元素名’)
- 创建新的文本节点:document.createTextNode(‘文本内容’)
- 创建新的片段节点:document.createDocumentFragment()
- 创建新的注释节点:document.createComment(‘注释’)
-
高效创建节点
- innerHTML
- outerHTML
- innerText
- outerText
方法名 | 说明 |
---|---|
document.createElement(‘元素名’) | 创建新的元素节点 |
document.createTextNode(‘文本内容’) | 创建新的文本节点 |
document.createComment(‘注释’) | 创建新的注释节点 |
document.createDocumentFragment() | 创建新的片段节点 |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.li{list-style:none;height:50px;line-height:50px;width:100px;background-color:lightblue;text-align:center;}
</style>
</head>
<body>
<script>
(function(){
var ulEle = document.createElement('ul');
for(var i=0; i<3 ; i++){
var liEle = document.createElement('li');
liEle.className = 'li';
liEle.innerHTML = '第'+i+'个li';
ulEle.appendChild(liEle);
}
document.body.appendChild(ulEle);
})();
</script>
</body>
</html>
- 高效创建节点
当要创建的大批量的节点,使用createElement
这类的方法创建节点就非常麻烦。
方法 | 说明 |
---|---|
innerHTML | 用来设置或获取当前标签的起始和结束里面的内容,在读模式下返回与调用元素的所有节点包括元素节点、注释节点、和文本节点对应的HTML标记;而在写模式下innerHTML会根据指定的值创建新的DOM树,然后用这个DOM树安全替换调用元素原先的所有子节点。 |
outerHTML | 返回调用它的元素及所有子节点的HTML标签 |
在写模式下会根据指定的HTML字符串创建新的outer指数,然后用outer指数完全替换调用的元素
2.1 document.createElement
该方法创建的 HTML5标签 是可以兼容IE8以下的浏览器,并在页面中正常显示该标签所设置的样式;参数可以是大小也可以是小写,但是多数情况下建议使用小写。
var ulEle = document.createElement('ul');
var liEle = document.createElement('li');
解决HTML5新元素不被IE6-8识别
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<style>
/*html5*/
article {
font-size: 40px;
color: red;
}
</style>
<script>
(function() {
if (!
/*@cc_on!@*/
0) return;
var e = "abbr, article, aside, audio, canvas, datalist, details, dialog, eventsource, figure, footer, header, hgroup, mark, menu, meter, nav, output, progress, section, time, video".split(', ');
var i = e.length;
while (i--){
document.createElement(e[i]);
}
})();
</script>
</head>
<body>
<article>
You are my sunshine.
</article>
</body>
</html>
2.5 innerHTML
用来设置或获取当前元素里面的所有子节点。
innerHTML有两种模式
- 读模式:
在读模式下返回调用元素下的所有节点包括元素节点、注释节点、和文本节点对应的HTML标记;
<div id="content">
苟利国家生死以
<p>This is a <strong>paragraph</strong> with a list following it.</p>
<ul>
<li>Item1</li>
<li>Item2</li>
<li>Item3</li>
</ul>
</div>
<script type="text/javascript">
var content = document.getElementById("content");
console.log(content.innerHTML);
</script>
- 写模式:
写模式下innerHTML会根据指定的值创建新的DOM树,然后用这个DOM树 完全替换 调用元素原先的 所有 子节点。
<div id="content">
<p>苟利国家生死以</p>
</div>
<script>
var content = document.getElementById("content");
var str = "<p>This is a <strong>paragraph</strong> with a list following it.</p>"
+ "<ul>"
+ "<li>Item 1</li>"
+ "<li>Item 2</li>"
+ "<li>Item 3</li>"
+ "</ul>";
content.innerHTML = str;
</script>
2.6 outerHTML
用来设置或获取调用它的元素及所有子节点。
两种模式
- 读模式:
读取包括调用元素以及调用元素下的所有子节点。
<div id="content">
苟利国家生死以
<p>This is a <strong>paragraph</strong> with a list following it.</p>
<ul>
<li>Item1</li>
<li>Item2</li>
<li>Item3</li>
</ul>
</div>
<script type="text/javascript">
var content = document.getElementById("content");
console.log(content.outerHTML);
</script>
- 写模式:
用新的dom树,完全替换 包括调用元素以及调用元素下的所有节点。
<div id="content">
</div>
<script>
var content = document.getElementById("content");
var str = "<p>This is a <strong>paragraph</strong> with a list following it.</p>"
+ "<ul>"
+ "<li>Item 1</li>"
+ "<li>Item 2</li>"
+ "<li>Item 3</li>"
+ "</ul>";
content.outerHTML = str;
</script>
2.7 innerText
读取元素中的所有子节点中的文本或者用文本内容替换所有子节点。
- 读模式
只读取文本,去掉所有HTML标签。
<div id="content">
<p>This is a <strong>paragraph</strong> with a list following it.</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
<script>
var content = document.getElementById("content");
console.log(content.innerText);
</script>
- 写模式
使用文本替换调用元素下的所有子节点。
<div id="content">
<p>This is a <strong>paragraph</strong> with a list following it.</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
<script>
var content = document.getElementById("content");
content.innerText = "<p>This is a paragraph.</p>";
</script>
firefox哪个版本都不支持innerText属性
解决办法在火狐浏览器下使用textContent
代替innerText
兼容firefox和chrome的写法
var content = document.getElementById("content");
function getInnerText(element){
return (typeof element.textContent == "string") ? element.textContent : element.innerText;
}
function setInnerText(element, text){
if (typeof element.textContent == "string"){
element.textContent = text;
} else {
element.innerText = text;
}
}
setInnerText(content, "Hello world!");
console.log(getInnerText(content));
2.8 outerText
功能与outerText类似,只不过内容变成了文本。
不建议使用
三、遍历节点
遍历节点浏览器提供了API
-
documentElement 返回文档HTML这个·节点
-
节点树(节点包括:html元素,文本,注释等)
-
元素树: 仅包含元素节点的树结构
3.1 节点树
该方法遍历有一个问题,标签直接的空格也算是一个节点。
遍历dom数需要判断当前节点是否问元素
语法: node.nodeType 属性返回节点的类型。
兼容:所有主流浏览器都支持。
节点类型 | 描述 | 返回值 |
---|---|---|
element | 元素 | 1 |
attr | 属性 | 2 |
text | 文本 | 3 |
comment | 注释 | 8 |
document | 代表整个文档(dom树的根节点) | 9 |
遍历节点树
var str = '';
function travel(space, node){
if(node.nodeType == 1){ 需要判断当前节点是否为元素
str += space + node.tagName +"<br/>";
}
for(var i=0, len=node.childNodes.length; i<len ; i++){
arguments.callee(space+'|-', node.childNodes[i]);
}
}
travel('',document);
document.write(str);
3.2 元素树(推荐使用)
var str = '';
function travel(space, node){
str += space + node.tagName + '<br/>';
for(var i=0, len=node.childElementCount ; i<len ; i++){
arguments.callee(space+'|-', node.children[i]);
}
}
travel('',document);
document.write(str);
3.3 类数组对象 NodeList(节点集合)
NodeList对象是节点的集合。
在节点树中通过childNodes
获取到的就是是一个类数组对象NodeList
- NodeList用于保存一组有序的节点
- 可以通过方括号来访问NodeList的值,有item方法和length属性
- 不是Array实例,没有数组对象的方法
如果要进行操作,可以将NodeList先转换为数组。
案例:将类数组对象转换为数组
<ul id="box">
<li>节点一</li>
<li>节点二</li>
<li>节点三</li>
</ul>
<script>
var box = document.getElementById("box");
var nodes = box.childNodes;
function makeArray(nodeList){
var arr = null;
try {
return Array.prototype.slice.call(nodeList); IE不支持该方法。
}catch (e){
arr = new Array();
for(var i = 0, len = nodeList.length; i < len; i++){
arr.push(nodeList[i]);
}
}
return arr;
}
var newNodeList = makeArray(nodes);
newNodeList.push("<li>节点四</li>");
console.log(newNodeList);
</script>
3.4 类数组对象 HTMLCollection(HTML元素集合)
以下操作会返回HTMLCollection类数组对象
- Ele.getElementsByTagName()
- document.scripts
- document.links
- document.images
- document.forms
- select.options
<div id="div">
<p>
<span>
<a href="">1</a>
<a href="">2</a>
<a href="">3</a>
</span>
<span>
<a href="">4</a>
<a href="">5</a>
<a href="">6</a>
</span>
<span>
<a href="">7</a>
<a href="">8</a>
<a href="">9</a>
</span>
</p>
</div>
<script>
var spanEle = document.getElementsByTagName('span');
console.log(spanEle);
</script>
nameItem() 方法
HTMLCollection对象的namedItem()方法,返回集合中具有指定name属性或id属性的第一个元素。
3.5 类数组对象 NameNodeMap (元素属性集合)
- ele.attributes
var div = document.getElementById('div');
console.log(div.attributes);
四、查找节点
IE6~IE8和最新浏览器都可以使用:
- getElementById() 必须通过document调用,只返回单个对象
- getElementsByName() 必须通过document调用
- getElementsByTagName()
最新浏览器可以使用:
- getElementsByClassName()
- querySelector()
- querySelectorAll()
4.1 getElementById()的bug
在低版本IE浏览器中document.getElementById()
会获取到指定name
属性的值。
<span name="target">错误的</span>
<div id="target">正确的</div>
<script>
var divEle = document.getElementById('target');
获取到的是<span name="target">错误的</span>
</script>
修复bug
var getElementById = function(id) {
var el = document.getElementById(id);
if(!+"\v1") {
if(el && el.id === id) {
return el;
} else {
var els = document.all[id],
n = els.length;
for (var i = 0; i < n; i++) {
if (els[i].id === id) {
return els[i];
}
}
}
}
return el;
};
!+"\v1"
:通过各个浏览器对转义字符不同的处理来判断是否为ie浏览器。
( !+"\v1" )
IE中:\v无转义,识别为v,+"\v1"转换数字失败,得到NaN,!表示非,最后解析为true。
其他浏览器,\v转义,类似空格,转换数字后得到1,转非解析为false,不执行后面的代码。
4.2 getElementsByTagName()
- 通过
getElementsByTagName()
获取到的是类数组对象 document.getElementsByTagName()
或者element.getElementsByTagName()
都可以。document.getElementsByTagName('*')
可以获得文档中所有的元素。document.getElementsByTagName('!')
可以获得文档中所有的注释。
4.3 getElementsByClassName()
该方法返回的是一个类数组对象HTMLCollection。
getElementsByClassName可以传入多个类名,类名之间用空格隔开。
<ul id="myUl">
<li class="light">1</li>
<li class="dark light">2</li>
<li class="light">3</li>
</ul>
<script>
var ul = document.getElementById('myUl');
var lis1 = ul.getElementsByClassName('light');
var lis2 = ul.getElementsByClassName('light dark');
var lis3 = ul.getElementsByClassName('dark light');
console.log(lis1);
console.log(lis2);
console.log(lis3);
</script>
兼容IE8以及以前的浏览器和现在主流浏览器
var getElementsByClassName = function(opts) {
var searchClass = opts.searchClass; // 存储要查找的类名
var node = opts.node || document; // 存储要出查找的范围
var tag = opts.tag || '*'; // 存储一定范围内要查找的标签
var result = [];
// 判断浏览器支不支持getElementsByClassName方法
if (document.getElementsByClassName) { // 如果浏览器支持
var nodes = node.getElementsByClassName(searchClass);
if (tag !== "*") {
for (var i = 0; node = nodes[i++];) {
if (node.tagName === tag.toUpperCase()) {
result.push(node);
}
}
} else {
result = nodes;
}
return result;
} else { // 使用IE8以下的浏览器能够支持该属性
var els = node.getElementsByTagName(tag);
var elsLen = els.length;
var i, j;
var pattern = new RegExp("(^|\\s)" + searchClass + "(\\s|$)");
//或者利用单词边界 var pattern = new RegExp("\\b" + searchClass + "\\b");
for (i = 0, j = 0; i < elsLen; i++) {
if (pattern.test(els[i].className)) { // 检测正则表达式
result[j] = els[i];
j++;
}
}
return result;
}
}
4.4 querySelector() 和 querySelectorAll()
- 主流浏览器都支持,IE8是率先支持的。。。
- 传入的选择器值要符合CSS规则。
- 可以通过
document
调用,也可以通过element
调用。 querySelector()
找不到元素返回null。querySelectorAll()
找不到元素返回空的集合
querySelector() 返回一个元素,如果有多个返回第一个。
<input type="text" class="foo:bar" value="请输入内容" />
<div id="myDiv">
You are my sunshine.
<ul id="myUl">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script type="text/javascript">
var divEle = document.querySelector('#myDiv');
console.log(divEle);
var liEle = divEle.querySelector('ul#myUl>li');
console.log(liEle);
</script>
querySelectorAll() 返回元素集合
<div id="myDiv">
You are my sunshine.
<ul id="myUl">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</div>
<script type="text/javascript">
var liEle = document.querySelectorAll('ul#myUl>li');
console.log(liEle);
</script>
querySelectorAll()返回的是一个静态的NodeList对象,而getElementsByClassName()和getElementByTagName()返回的是动态NodeList对象。
静态NodeList
<body>
<div></div>
<div></div>
<div></div>
</body>
<script>
var divs = document.querySelectorAll("div");
var i = 0;
while(i < divs.length){
document.body.appendChild(document.createElement("div"));
i++;
}
</script>
正常执行,循环3次插入元素就结束。
动态NodeList
<body>
<div></div>
<div></div>
<div></div>
</body>
<script>
var divs = document.getElementsByTagName("div");
var i = 0;
while(i < divs.length){
document.body.appendChild(document.createElement("div"));
i++;
}
</script>
会陷入死循环,divs.length
的值会一直增长。
五、插入节点
- appendChild()
- insertBefore()
- replaceChild()
- cloneNode()
- norrmalize()
- SplitText()
5.1 appendChild()
在调用元素的最后一个子节点(节点包括文本)后添加节点,该方法返回新的节点。
<div id="wrap">
<ul id="myUl">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</div>
<script type="text/javascript">
var myUl = document.querySelector('#myUl');
var txt = document.createTextNode('4');
var newLi = document.createElement('li');
newLi.appendChild(txt);
myUl.appendChild(newLi);
</script>
5.2 insertBefore()
在调用元素的指定已有的子节点之前插入新节点。
<ul id="myUl">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var myUl = document.querySelector('#myUl');
var txt = document.createTextNode('4');
var newLi = document.createElement('li');
newLi.appendChild(txt);
var li2 = myUl.children[1];
myUl.insertBefore(newLi,li2);
</script>
将新的节点插入到子节点最后:模拟appendChild()
myUl.insertBefore(newLi,null);
将新的节点插入到开头
myUl.insertBefore(newLi,myUl.firstElementChild);
5.3 replaceChild()
该方法用新节点替换某个子节点。
语法:replaceChild(要插入的节点,被替换的节点)
返回被替换的节点,被替换的节点会被从dom树移除。
5.3 cloneNode()
创建节点的拷贝,并返回该副本
拷贝的节点要有父节点,如果没有父节点,要通过appendChild()、insertBefore()、replaceChild()等方法进行添加。
接收一个参数默认是false,如果为true即深度复制。深度复制会复制子节点。
5.4 norrmalize()
- 移除空的文本节点,并连接相邻的文本节点
5.5 SplitText()
splitText(offset)
方法按照指定的 offset 把文本节点分割为两个节点
返回新的文本节点
六、删除节点
removeChild():删除某个节点中指定的子节点,一定要有参数
removeNode():IE的私有
将目标节点从文档中删除,返回目标节点
参数布尔值,默认false(true深度删除)
removeChild和removeNode()方法
innerHTML:将值设置为空,就可以删除调用元素下面的子元素。