变动事件_DOM2级的变动事件(mutation)
DOM2级定义了如下变动事件:
- DOMSubtreeModified:
在DOM结构中发生任何变化时触发。这个事件在其他任何事件触发后都会触发。 - DOMNodeInserted:
在一个节点作为子节点被插入到另一个节点中时触发 - DOMNodeRemoved:
在节点从其父节点中被移除时触发 - DOMNodeInsertIntoDocument:
在一个节点被直接插入文档或通过子树间接插入文档之后触发。这个事件在DOMNodeInserted之后触发 - DOMNodeRemovedFromDocument:
在一个节点被直接从文档中移除或通过子树间接从文档中移除之前触发。这个事件在DOMNodeRemoved之后触发 - DOMAttrModified:在特性被修改之后触发
- DOMCharacterDataModified:在文本节点的值发生变化时触发。
使用下列代码可以检测出浏览器是否支持变动事件:
var isSupported = document.implementation.hasFeature('MutationEvents','2.0');
IE8之前的版本不支持任何变动事件
由于DOM3级事件模块作废了很多变动事件
- 删除节点
在使用removeChild()和replaceChild()从DOM中删除节点时:
首先触发DOMNodeRemoved事件
这个事件的目标(event.target)是被删除的节点
</head> <body style='height:3000px;'> <ul id='myList'> <li>Item 1</li> </ul> <div id="div1"></div> <script> 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; } }, getEvent: function(event){//获得事件对象 return event || window.event; }, getTarget: function(event){//获得事件元素 return event.target || event.srcElement; }, preventDefault: function(){//取消默认事件行为 if (event.preventDefault) { event.preventDefault(); }else { event.returnValue = false; } }, removeHandler: function(element,type,handler){//取消事件 if (element.removeEventListener) { element.removeEventListener(type,handler,false) }else if (element.dettchEvent) { element.dettchEvent('on'+type,handler); }else { element['on'+type] = null; } }, stopPropagation: function(event){//取消冒泡机制 if (event.stopPropagation) { event.stopPropagation(); }else { event.cancleBubble = true; } }, getRelatedTarget: function(event){ if (event.relatedTarget) { return event.relatedTarget;//标准下返回相关元素 }else if (event.toElement) { return event.toElement;//mouseout事件触发,保存相关元素 }else if (event.fromElement) { return event.fromElement;//mouseover事件触发,保存相关元素 } }, getButton: function(event){//鼠标按钮兼容 if (document.implementation.hasFeature('MouseEvents','2.0'))//标准下 { return event.button; }else { switch (event.button)//非标准下 { case 0: case 1: case 3: case 5: case 7: return 0; case 2: case 6: return 2; case 4: return 1; } } }, getWheelDelta: function(event){//滚轮事件兼容 //所以要兼容,写两个函数函数 //client的兼容性必须先写出来 if (event.wheelDelta) { /* 兼容opear9.5以前版本的正负相反,mousewheel */ return (window.client.engine.opera && window.client.engine.opera < 9.5 ? -event.wheelDelta : event.wheelDelta); }else { /* 兼容firefox正负和3的倍数的问题,DOMMouseScroll */ return -event.detail*40; } }, getCharCode: function(event){//键盘事件兼容 if (typeof event.charCode == 'number')//首先检测按键有没有代表的字符,如果没有就没有charCode,为undefined { return event.charCode; }else { return event.keyCode } } } EventUtil.addHandler(window,'load',function(event){ var list = document.getElementById('myList'); EventUtil.addHandler(document,'DOMSubtreeModified',function(event){ console.log('--------------------------------------------'); console.log( 'DOMSubtreeModified:改变DOM结构' ); console.log(event.type); console.log(event.target); }); EventUtil.addHandler(document,'DOMNodeRemoved',function(event){ console.log('--------------------------------------------'); console.log( 'DOMNodeRemoved:删除节点' ); console.log(event.type); console.log(event.target); console.log(event.relatedNode); }); EventUtil.addHandler(list.firstChild,'DOMNodeRemovedFromDocument',function(event){ console.log('--------------------------------------------'); console.log( 'DOMNodeRemovedFromDocument:从文档中删除节点' ); console.log(event.type); console.log(event.target); }); list.parentNode.removeChild(list); }); </script>
执行顺序是:
- 删除节点:DOMNodeRemove
- 从document中删除节点:DOMNodeRemovedFromDocument
- 改变DOM结构:DOMSubtreeModified
- 插入节点
在使用appendChild()、replaceChild()、insertBefore()向DOM中插入节点时,
执行顺序是:
- DOMNodeInserted事件:这个事件的目标是插入的节点,而event.relatedNode属性中包含一个对父节点的引用。这个事件是冒泡的
- DOMNodeInsertedIntoDocument事件:这个事件不冒泡,因此必须在插入节点之前为它添加这个事件处理程序。这个事件的目标是被插入的节点。
- DOMSubtreeModified:DOM结构发生变化触发
例子:
<body style='height:3000px;'> <div id="div1"></div> <script> 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; } }, getEvent: function(event){//获得事件对象 return event || window.event; }, getTarget: function(event){//获得事件元素 return event.target || event.srcElement; }, preventDefault: function(){//取消默认事件行为 if (event.preventDefault) { event.preventDefault(); }else { event.returnValue = false; } }, removeHandler: function(element,type,handler){//取消事件 if (element.removeEventListener) { element.removeEventListener(type,handler,false) }else if (element.dettchEvent) { element.dettchEvent('on'+type,handler); }else { element['on'+type] = null; } }, stopPropagation: function(event){//取消冒泡机制 if (event.stopPropagation) { event.stopPropagation(); }else { event.cancleBubble = true; } }, getRelatedTarget: function(event){ if (event.relatedTarget) { return event.relatedTarget;//标准下返回相关元素 }else if (event.toElement) { return event.toElement;//mouseout事件触发,保存相关元素 }else if (event.fromElement) { return event.fromElement;//mouseover事件触发,保存相关元素 } }, getButton: function(event){//鼠标按钮兼容 if (document.implementation.hasFeature('MouseEvents','2.0'))//标准下 { return event.button; }else { switch (event.button)//非标准下 { case 0: case 1: case 3: case 5: case 7: return 0; case 2: case 6: return 2; case 4: return 1; } } }, getWheelDelta: function(event){//滚轮事件兼容 //所以要兼容,写两个函数函数 //client的兼容性必须先写出来 if (event.wheelDelta) { /* 兼容opear9.5以前版本的正负相反,mousewheel */ return (window.client.engine.opera && window.client.engine.opera < 9.5 ? -event.wheelDelta : event.wheelDelta); }else { /* 兼容firefox正负和3的倍数的问题,DOMMouseScroll */ return -event.detail*40; } }, getCharCode: function(event){//键盘事件兼容 if (typeof event.charCode == 'number')//首先检测按键有没有代表的字符,如果没有就没有charCode,为undefined { return event.charCode; }else { return event.keyCode } } } EventUtil.addHandler(window,'load',function(event){ var oDiv = document.getElementById('div1'); var oSpan = document.createElement('span'); oSpan.id = 'span1'; function add(){ oDiv.appendChild(oSpan); } function DOMSubtreeModified(event){ event = EventUtil.getEvent(event); console.log('----------------------------------------'); console.log(event.type); console.log(event.target); //console.log(event.relatedNode); } function DOMNodeInserted(event){ event = EventUtil.getEvent(event); console.log('----------------------------------------'); console.log(event.type); console.log(event.target); console.log(event.relatedNode); } function DOMNodeInsertedIntoDocument(event){ event = EventUtil.getEvent(event); console.log('----------------------------------------'); console.log(event.type); console.log(event.target); //console.log(event.relatedNode); } EventUtil.addHandler(oDiv,'click',add); EventUtil.addHandler(document,'DOMSubtreeModified',DOMSubtreeModified); EventUtil.addHandler(document,'DOMNodeInserted',DOMNodeInserted); EventUtil.addHandler(oSpan,'DOMNodeInsertedIntoDocument',DOMNodeInsertedIntoDocument); }); </script> </body>
由于DOMSubtreeModified和DOMNodeInserted事件是冒泡的,所以它们的事件处理程序是添加到文档上的,
在将oSpan插入到其父节点之前,先将DOMNodeInsertedIntoDocument事件的事件处理程序添加给oSpan。
最后用appendChild()来添加这个oSpan;
此时事件开始依次触发。
首先是在新的oSpan元素上触发DOMNodeInserted事件,其relatedNode是oDiv元素,
然后触发oSpan元素上的DOMInsertedIntoDocument事件
最后触发oDiv上的DOMSubtreeModified事件