0%

DOM编程

DOM编程

动态脚本

1
2
3
4
5
6
7
8
9
10
11
function loadScriptString(code){
var script=document.createElement("script");
script.type="text/javascript";
try{
script.appendChild(document.createTextNode(code));//旧版本的IE浏览器可能出问题
}catch(ex){
script.text=code;
}
document.body.appendChild(script);
}
loadScriptString("function sayHi(){alert('hi');}");

动态样式

1
2
3
4
5
6
7
8
9
10
11
12
function loadStyleString(css){
let style=document.createElement("style");
style.type="text/css";
try{
style.appendChild(document.createTextNode(css));
}catch(ex){
style.styleSheet.cssText=css;
}
let head=document.getElementsByTagName("head")[0];
head.appendChild(style);
}
loadStyleString("body[background-color:red}");

对于IE,要小心使用styleSheet.cssText,如果重用同一个style元素并设置该属性超过一次,则可能导致浏览器崩溃,将cssText设置为空字符串也可能导致浏览器崩溃

表单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let table=document.createElement("table");
table.border=1;
table.width="100%";
let tbody=document.createElement("tbody");
table.appendChild(tbody);

table.insertRow(0);
tbody.rows[0].insertCell(0);
tbody.rows[0].cells[0].appendChild(document.createTextNode("Cell 1,1"));
tbody.rows[0].insertCell(1);
tbody.rows[0].cells[1].appendChild(document.createTextNode("Cell 2,1"));

table.insertRow(1);
tbody.rows[1].insertCell(0);
tbody.rows[1].cells[0].appendChild(document.createTextNode("Cell 1,2"));
tbody.rows[1].insertCell(1);
tbody.rows[1].cells[1].appendChild(document.createTextNode("Cell 2,2"));

document.body.appendChild(table);

NodeList

1
2
3
4
5
let divs=document.getElementsByTagName("div");
for(let i=0,len=divs.length;i<len;i++){
let div=doocument.createElement("div");
document.body.appendChild(div);
}//避免导致无穷循环

Mutationobserver接口

在DOM被修改时异步执行回调,使用MutationObserver可以观察整个文档,DOM树的一部分或某个元素

MutationObserver的实例通过调用MutationObserver构造函数并传入一个回调函数创建

1
let observer=new MutationObserver(()=>console.log('DOM was mutated'));

observe()方法

接收两个参数:要观察其变化的DOM节点,以及一个MutationObserverInit对象(用于控制观察哪些方面的变化,是一个键值对形式配置选项的字典)

1
2
let observer=new MutationObserver(()=>console.log('<body> attributes changed'));
observer.observe(document.body,{attributes:true});

body元素上任何属性发生变化都会被这个MutationObserver实例发现,然后异步执行注册的回调函数,body元素后代修改或其他非属性修改不会触发回调进入任务

1
2
3
4
5
6
let observer=new MutationObserver(()=>console.log('<body> attributes changed'));
observer.observe(document.body,{attributes:true});
document.body.className='foo';
console.log('Changed body class');
//Changed body class
//<body> attributes changed

回调与MutationRecord

每次回调都会收到一个MutationRecord实例的数组,包含的信息发生了什么变化以及DOM哪一部分受到影响

1
2
3
4
5
6
7
8
9
let observer=new MutationObserver((mutationRecords)=>console.log(mutationRecords));[
observer.observe(document.body,{attributes:true});
document.body.setAttributeNS('foo','bar','baz');
//连续修改多个MutationRecord实例,回调函数就会受到包含这些实例的数组,顺序为变化事件的顺序
let observer=new MutationObserver((mutationRecords)=>console.log(mutationRecords));
observer.observe(document.body,{attributes:true});
document.body.className='foo';
document.body.className='bar';
//[MutationRecord, MutationRecord]

disconnect()方法

会停止此后变化事件的回调,也会抛弃已经加入任务队列要异步执行的回调

1
2
3
4
5
6
7
8
9
10
11
let observer=new MutationObserver(()=>console.log('<body>attributes changed'));
observer.observe(document.body,{attributes:true});
document.body.className='foo';
observer.disconnect();
document.body.className='bar';//无日志输出
//让已经入队的回调函数执行完毕后再调用disconnect()
setTimeout(()=>{
observer.disconnect();
document.body.className='bar';

},0);

复用MutationObserver

1
2
3
4
5
6
7
8
9
10
let observer=new MutationObserver((mutationRecords)=>console.log(mutationRecords.map((x)=>x.target)));
let childA=document.createElement('div');
childB=document.createElement('span');
document.body.appendChild(childA);
document.body.appendChild(childB);
observer.observe(childA,{attributes:true});
observer.observe(childB,{attributes:true});
childA.setAttribute('foo','bar');
childB.setAttribute('foo','bar');
//[div, span]

重用MutationObserver

调用diaconnect()不会结束MutationObserver的生命,还可以重新使用这个观察者,再将它关联到新的目标结点

MutationObserverInit与观察范围

观察属性

设置attributes为true,观察所有属性

1
2
3
4
5
6
7
8
9
let observer=new MutationObserver((mutationRecords)=>console.log(mutationRecords));
observer.observe(document.body,{attributes:true});
//添加属性
document.body.setAttribute('foo','bar');
//修改属性
document.body.setAttribute('foo','baz');
//移除属性
document.body.removeAttribute('foo');

用attributeFilter添加白名单属性

1
2
3
4
5
6
let observer=new MutationObserver((mutationRecords)=>console.log(mutationRecords));
observer.observe(document.body,{attributeFilter:['foo']});
//添加白名单属性
document.body.setAttribute('foo','bar');
//添加被排除的属性
document.body.setAttribute('baz','qux');

在记录中保存属性原来的值

1
2
3
4
5
6
let observer=new MutationObserver((mutationRecords)=>console.log(mutationRecords.map((x)=>x.oldValue)));
observer.observe(document.body,{attributeOldValue:true});
document.body.setAttribute('foo','bar');
document.body.setAttribute('foo','baz');
document.body.setAttribute('foo','qux');
//[null, 'bar', 'baz']

观察字符数据

1
2
3
4
5
6
7
8
9
10
11
12
13
let observer=new MutationObserver(document.body.firstChild,{characterData:true});
document.body.firstChild.textContent='foo';//创建要观察的文本节点
observer.observe(document.body.firstChild,{characterData:true});
document.body.firstChild.textContent='foo';
document.body.firstChild.textContent='bar';
document.body.firstChild.textContent='baz';
//使用characterDataOldValue
let observer=new MutationObserver((mutationRecords)=>console.log(mutationRecords.map((x)=>x.oldValue)));
document.body.firstChild.textContent='foo';//创建要观察的文本节点
observer.observe(document.body.firstChild,{characterDataOldValue:true});
document.body.firstChild.textContent='foo';
document.body.firstChild.textContent='bar';
document.body.firstChild.textContent='baz';

观察子节点

1
2
3
4
5
6
7
8
document.body.innerHTML='';
let observer=new MutationObserver((mutationRecords)=>console.log(mutationRecords));
//创建两个初始子节点
document.body.appendChild(document.createElement('div'));
document.body.appendChild(document.createElement('span'));
observer.observe(document.body,{childList:true});
document.body.insertBefore(document.body.lastChild,document.body.firstChild);
//发生两次变化,先移除节点再添加节点

观察子树

1
2
3
4
5
document.body.innerHTML='';
let observer=new MutationObserver((mutationRecords)=>console.log(mutationRecords));
document.body.appendChild(document.createElement('div'));
observer.observe(document.body,{attributes:true,subtree:true});
document.body.firstChild.setAttribute('foo','baz');

异步回调与记录队列

takeRecords()方法

清空记录队列,取出并·返回其中的所有MutationRecord实例,可以用在希望断开与观察目标的联系,但又希望处理由于disconnect()而被抛弃的记录队列中的MutationRecord实例

1
2
3
4
5
6
7
8
let observer=new MutationObserver((mutationRecords)=>console.log(mutationRecords));
observer.observe(document.body,{attributes:true});
document.body.className='foo';
document.body.className='bar';
console.log(observer.takeRecords());
console.log(observer.takeRecords());
//[MutationRecord, MutationRecord]
//[]