JavaScript操作DOM的那些坑


js在操作DOM中存在着許多跨瀏覽器方面的坑,本文花了我將近一周的時間整理,我將根據實例整理那些大大小小的“坑”。

DOM的工作模式是:先加載文檔的靜態內容、再以動態方式對它們進行刷新,動態刷新不影響文檔的靜態內容。

PS:IE 中的所有 DOM 對象都是以 COM 對象的形式實現的,這意味着 IE 中的 DOM可能會和其他瀏覽器有一定的差異。

Node 接口

特性/方法 類型/返回類型 說 明
nodeName String 節點的名字;根據節點的類型而定義
nodeValue String 節點的值;根據節點的類型而定義
nodeType Number 節點的類型常量值之一
ownerDocument Document 返回某元素的根元素
firstChild Node 指向在childNodes列表中的第一個節點
lastChild Node 指向在childNodes列表中的最后一個節點
childNodes NodeList 所有子節點的列表
previousSibling Node 返回選定節點的上一個同級節點,若不存在,則返回null
nextSibling Node 返回被選節點的下一個同級節點,若不存在,則返回null
hasChildNodes() Boolean 如果當前元素節點擁有子節點,返回true,否則返回false
attributes NamedNodeMap 返回包含被選節點屬性的 NamedNodeMap
appendChild(node) node 將node添加到childNodes的末尾
removeChild(node) node 從childNodes中刪除node
replaceChild(newnode, oldnode) Node 將childNodes中的oldnode替換成newnode
insertBefore Node 在已有子節點之前插入新的子節點

firstChild 相當於 childNodes[0]lastChild 相當於childNodes[box.childNodes.length - 1]

nodeType返回結點的類型

--元素節點返回1 --屬性節點返回2 --文本節點返回3 
--注釋元素返回8
--文檔元素返回9
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>無標題文檔</title> <script> window.onload = function() {  var element = document.getElementById("myspan");  var text = element.firstChild;  var property = document.getElementById("myspan").getAttributeNode("id");  alert("這是元素節點的返回值:"+ element.nodeType);//元素節點返回1  alert("這是文本節點的返回值:"+ text.nodeType);//文本節點返回3  alert("這是屬性節點的返回值:"+ property.nodeType);//屬性節點返回2 } </script> </head> <body> <div> <span id="myspan">文本節點</span> </div> </body> </html>

innerHTML 和 nodeValue

對於文本節點,nodeValue 屬性包含文本。

對於屬性節點,nodeValue 屬性包含屬性值。

nodeValue 屬性對於文檔節點和元素節點是不可用的。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>無標題文檔</title>
<script>
window.onload = function()
{
  var element = document.getElementById("myspan");
  var text = element.firstChild;
  var property = document.getElementById("myspan").getAttributeNode("id");
  alert("這是元素節點的返回值:"+ element.nodeValue);//本身就沒有意義,這里是試驗下的,返回的是null
  alert("這是文本節點的返回值:"+ text.nodeValue);//返回的是文本值  文本節點
  alert("這是屬性節點的返回值:"+ property.nodeValue);//返回的是屬性值,這里是id的屬性值  myspan
}
</script>
</head>
<body>
<div>
<span id="myspan">文本節點</span>
</div>
</body>
</html>
 

兩者區別

box.childNodes[0].nodeValue = '<strong>abc</strong>';//結果為:<strong>abc</strong> abcbox.innerHTML = '<strong>abc</strong>';//結果為:abc 

nodeName屬性獲得結點名稱

--對於元素節點返回的是標記名稱,如:<a herf><a>返回的是"A" --對於屬性節點返回的是屬性名稱,如:id="test" 返回的是id --對於文本節點返回的是#text

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>無標題文檔</title>
<script>
window.onload = function()
{
  var element = document.getElementById("span");
  var text = element.firstChild;
  var property = document.getElementById("span").getAttributeNode("id");
  alert("這是元素節點的返回值:"+ element.nodeName);//返回的標簽名SPAN,注意是大寫的
  alert("這是文本節點的返回值:"+ text.nodeName);//返回的#text
  alert("這是屬性節點的返回值:"+ property.nodeName);//返回的是屬性名,這里是id
}
</script>
</head>
<body>
<div>
<span id="span">文本節點</span>
</div>
</body>
</html>

tagName

document.getElementsByTagName(tagName):返回一個數組,包含對這些結點的引用 

getElementsByTagName()方法將返回一個對象數組 HTMLCollection(NodeList),這個數組保存着所有相同元素名的節點列表。

document.getElementsByTagName('*');//獲取所有元素 

PS:IE 瀏覽器在使用通配符的時候,會把文檔最開始的 html 的規范聲明當作第一個元素節點。

document.getElementsByTagName('li');//獲取所有 li 元素,返回數組 document.getElementsByTagName('li')[0];//獲取第一個 li 元素,HTMLLIElement document.getElementsByTagName('li').item(0);//獲取第一個 li 元素,HTMLLIElement document.getElementsByTagName('li').length;//獲取所有 li 元素的數目 

節點的絕對引用:

返回文檔的根節點:document.documentElement
返回當前文檔中被擊活的標簽節點:document.activeElement
返回鼠標移出的源節點:event.fromElement 返回鼠標移入的源節點:event.toElement 返回激活事件的源節點:event.srcElement 

節點的相對引用:(設當前對節點為node)

返回父節點:node.parentNode || node.parentElement(IE)
返回子節點集合(包含文本節點及標簽節點):node.childNodes
返回子標簽節點集合:node.children
返回子文本節點集合:node.textNodes
返回第一個子節點:node.firstChild
返回最后一個子節點:node.lastChild
返回同屬下一個節點:node.nextSibling
返回同屬上一個節點:node.previousSibling 

節點信息

是否包含某節點:node.contains()

是否有子節點node.hasChildNodes() 

創建新節點

createDocumentFragment()--創建文檔碎片節點
createElement(tagname)--創建標簽名為tagname的元素
createTextNode(text)--創建包含文本text的文本節點

獲取鼠標點擊事件的位置

document.onclick = mouseClick; function mouseClick(ev){ ev = ev || window.event;//window.event用來兼容IE var x = 0; var y = 0; if(ev.pageX){ x = ev.pageX; y = ev.pageY; }else if(ev.clientX){ var offsetX = 0 , offsetY = 0; if(document.documentElement.scrollLeft){ offsetX = document.documentElement.scrollLeft; offsetY = document.documentElement.scrollTop; }else if(document.body){ offsetX = document.body.scrollLeft; offsetY = document.body.scrollTop; } x = ev.clientX + offsetX; y = ev.clientY + offsetY; } alert("你點擊的位置是 x="+ x + " y=" + y); } 

以下所描述的屬性在chromeSafari 都很給力的支持了。

問題一:FirefoxChromeSafariIE9都是通過非標准事件的pageXpageY屬性來獲取web頁面的鼠標位置的。pageX/Y獲取到的是觸發點相對文檔區域左上角距離,以頁面為參考點,不隨滑動條移動而變化

問題二:在IE 中,event 對象有 x, y 屬性(事件發生的位置的 x 坐標和 y 坐標)火狐中沒有。在火狐中,與event.x 等效的是 event.pageXevent.clientXevent.pageX 有微妙的差別(當整個頁面有滾動條的時候),不過大多數時候是等效的。

offsetX:IE特有,chrome也支持。鼠標相比較於觸發事件的元素的位置,以元素盒子模型的內容區域的左上角為參考點,如果有boder,可能出現負值

問題三:
scrollTop為滾動條向下移動的距離,所有瀏覽器都支持document.documentElement

其余參照:http://segmentfault.com/a/1190000002559158#articleHeader11

參照表

+為支持,-為不支持):

offsetX/offsetY:W3C- IE+ Firefox- Opera+ Safari+ chrome+

x/y:W3C- IE+ Firefox- Opera+ Safari+ chrome+

layerX/layerY:W3C- IE- Firefox+ Opera- Safari+ chrome+

pageX/pageY:W3C- IE- Firefox+ Opera+ Safari+ chrome+

clientX/clientY:W3C+ IE+ Firefox+ Opera+ Safari+ chrome+

screenX/screenY:W3C+ IE+ Firefox+ Opera+ Safari+ chrome+

查看下方DEMO
你會發現offsetXFirefox下是undefined,在chromeIE則會正常顯示。

https://jsfiddle.net/f4am208m/embedded/result/

offsetLeft和style.left區別

1.style.left返回的是字符串,比如10px。而offsetLeft返回的是數值,比如數值10

2.style.left是可讀寫的,offsetLeft是只讀的

3.style.left的值需要事先定義(在樣式表中定義無效,只能取到在html中定義的值),否則取到的值是空的

getComputedStyle與currentStyle

getComputedStyle()接受兩個參數:要取得計算樣式的元素和一個偽元素,如果不需要偽元素,則可以是null。然而,在IE中,並不支持getComputedStyle,IE提供了currentStyle屬性。

getComputedStyle(obj , false ) 是支持 w3c (FF12、chrome 14、safari):在FF新版本中只需要第一個參數,即操作對象,第二個參數寫“false”也是大家通用的寫法,目的是為了兼容老版本的火狐瀏覽器。
缺點:在標准瀏覽器中正常,但在IE6/7/8中不支持

 window.onload=function(){ var oBtn=document.getElementById('btn'); var oDiv=document.getElementById('div1'); oBtn.onclick=function(){ //alert(oDiv.style.width); //寫在樣式表里無法讀取,只能得到寫在行內的 //alert(getComputedStyle(oDiv).width); //適用於標准瀏覽器 IE6、7、8不識別 //alert(oDiv.currentStyle.width); //適用於IE瀏覽器,標准瀏覽器不識別 if(oDiv.currentStyle){ alert(oDiv.currentStyle.width); }else{ alert(getComputedStyle(oDiv).width); } }; }; 

取消表單提交

<script type="text/javascript"> function listenEvent(eventObj,event,eventHandler){ if(eventObj.addEventListener){ eventObj.addEventListener(event,eventHandler,false); }else if(eventObj.attachEvent){ event = "on" + event; eventObj.attachEvent(event,eventHandler); }else{ eventObj["on" + event] = eventHandler; } } function cancelEvent(event){ if(event.preventDefault){ event.preventDefault();//w3c }else{ event.returnValue = true;//IE } } window.onload = function () { var form = document.forms["picker"]; listenEvent(form,"submit",validateFields); }; function validateFields(evt){ evt = evt ? evt : window.event; ... if(invalid){ cancelEvent(evt); } } </script> 

確定瀏覽器窗口的尺寸

對於主流瀏覽器來說,比如IE9FirefoxChromeSafari,支持名為innerWidthinnerHeight的窗口對象屬性,它返回窗口的視口區域,減去任何滾動條的大小。IE不支持innerWidthinnerHeight

<script type="text/javascript"> function size(){ var w = 0, h=0; if(!window.innerWidth){ w = (document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth); h = (document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight); }else{ w = window.innerWidth; h = window.innerHeight; } return {width:w,height:h}; } console.log(size());//Object { width: 1366, height: 633 } </script> 

實用的 JavaScript 方案(涵蓋所有瀏覽器):

var w=window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; var h=window.innerHeight || document.documentElement.clientHeight|| document.body.clientHeight; 

對於 IE 6、7、8的方案如下:

document.documentElement.clientHeight document.documentElement.clientWidth 

或者

document.body.clientHeight document.body.clientWidth 

Document對象的body屬性對應HTML文檔的<body>標簽。Document對象的documentElement屬性則表示 HTML文檔的根節點。

attributes 屬性

attributes 屬性返回該節點的屬性節點集合。

document.getElementById('box').attributes//NamedNodeMap document.getElementById('box').attributes.length;//返回屬性節點個數 document.getElementById('box').attributes[0]; //Attr,返回最后一個屬性節點 document.getElementById('box').attributes[0].nodeType; //2,節點類型 document.getElementById('box').attributes[0].nodeValue; //屬性值 document.getElementById('box').attributes['id']; //Attr,返回屬性為 id 的節點 document.getElementById('box').attributes.getNamedItem('id'); //Attr 

setAttribute 和 getAttribute

IE中是不認識class屬性的,需改為className屬性,同樣,在Firefox中,也是不認識className屬性的,Firefox只認識class屬性,所以通常做法如下:

element.setAttribute(class, value); //for firefox element.setAttribute(className, value); //for IE 

IE:可以使用獲取常規屬性的方法來獲取自定義屬性,也可以使用getAttribute()獲取自定義屬性
Firefox:只能使用getAttribute()獲取自定義屬性.

解決方法:統一通過getAttribute()獲取自定義屬性

document.getElementById('box').getAttribute('id');//獲取元素的 id 值 document.getElementById('box').id;//獲取元素的 id 值 document.getElementById('box').getAttribute('mydiv');//獲取元素的自定義屬性值 document.getElementById('box').mydiv//獲取元素的自定義屬性值, IE 不支持非 document.getElementById('box').getAttribute('class');//獲取元素的 class 值,IE 不支持 document.getElementById('box').getAttribute('className');//非 IE 不支持 

PS:在 IE7 及更低版本的IE瀏覽器中,使用 setAttribute()方法設置 classstyle 屬性是沒有效果的,雖然 IE8 解決了這個 bug,但還是不建議使用。

removeAttribute()方法

removeAttribute()可以移除 HTML 屬性。
document.getElementById('box').removeAttribute('style');//移除屬性 

PS:IE6 及更低版本不支持 removeAttribute()方法。

跨瀏覽器事件Event對象

<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style type="text/css"> #drop{ width: 300px; height: 200px; background-color: #ff0000; padding: 5px; border: 2px solid #000000; } #item{ width: 100px; height: 100px; background-color: #ffff00; padding: 5px; margin: 20px; border: 1px dashed black; } *[draggable = true]{ -moz-user-select: none; -webkit-user-select: none; cursor: move; } </style> </head> <body> <div> <p>將金黃色的小方塊拖到紅色的大方塊中,不兼容IE7及以下瀏覽器,兼容主流瀏覽器!</p> </div> <div id="item" draggable="true"></div> <div id="drop"></div> <script type="text/javascript"> function listenEvent(target,type,handler){ if(target.addEventListener){//w3c target.addEventListener(type,handler,false); }else if(target.attachEvent){//IE type = "on" + type; target.attachEvent(type,handler);//IE }else{ target["on" + type] = handler; } } //取消事件 function cancelEvent(e){ if(e.preventDefault){ e.preventDefault();//w3c }else{ e.returnValue = false;//IE } } //取消傳遞 function cancelPropagation(e){ if(e.stopPropagation){ e.stopPropagation();//w3c }else{ e.cancelBubble = true;//IE } } window.onload = function () { var target = document.getElementById('drop'); listenEvent(target,'dragenter',cancelEvent); listenEvent(target,"dragover",dragOver); listenEvent(target,'drop', function (evt) { cancelPropagation(evt); evt = evt || window.event; evt.dataTransfer.dropEffect = 'copy'; var id = evt.dataTransfer.getData('Text'); target.appendChild(document.getElementById(id)); }); var item = document.getElementById('item'); item.setAttribute("draggable",'true'); listenEvent(item,'dragstart', function (evt) { evt = evt || window.event; evt.dataTransfer.effectAllowed = 'copy'; evt.dataTransfer.setData('Text',item.id); }); }; function dragOver(evt){ if(evt.preventDefault) evt.preventDefault(); evt = evt || window.event; evt.dataTransfer.dropEffect = 'copy'; return false; } </script> </body> </html> 

dataTransfer 對象

屬性 描述
dropEffect 設置或獲取拖曳操作的類型和要顯示的光標類型
effectAllowed 設置或獲取數據傳送操作可應用於該對象的源元素
方法 描述
clearData 通過 dataTransfer 或 clipboardData 對象從剪貼板刪除一種或多種數據格式
getData 通過 dataTransfer 或 clipboardData 對象從剪貼板獲取指定格式的數據
setData 以指定格式給 dataTransfer 或 clipboardData 對象賦予數據

HTML5拖拽的瀏覽器支持

Internet Explorer 9、Firefox、Opera 12、Chrome 以及 Safari 5 支持拖放

為了使元素可拖動,需把 draggable 屬性設置為 true

<img draggable="true" /> 
事件 描述
dragstart 拖拽事件開始
drag 在拖動操作上
dragenter 拖動到目標上,用來決定目標是否接受放置
dragover 拖動到目標上,用來決定給用戶的反饋
drop 放置發生
dragleave 拖動離開目標
dragend 拖動操作結束

上述代碼的一些瀏覽器兼容性:

1.為了兼容IE,我們將`window.event`賦給 `evt`,其他瀏覽器則會正確將接收到的`event`對象賦給`evt`。 2.w3c使用addEventListener來為事件元素添加事件監聽器,而IE則使用attachEvent。addEventListener為事件冒泡到的當前對象,而attachEvent是window 3.對於事件類型,IE需要加`on + type`屬性,而其他瀏覽器則不用 4.對於阻止元素的默認事件行為,下面是w3c和IE的做法: e.preventDefault();//w3c e.returnValue = false;//IE 5.對於取消事件傳播,w3c和IE也有不同的處理機制: e.stopPropagation();//w3c e.cancelBubble = true;//IE 

跨瀏覽器獲取目標對象

//跨瀏覽器獲取目標對象 function getTarget(ev){ if(ev.target){//w3c return ev.target; }else if(window.event.srcElement){//IE return window.event.srcElement; } } 

對於獲取觸發事件的對象,w3cIE也有不同的做法:

event.target;//w3c event.srcElement;//IE 

我們可以使用三目運算符來兼容他們:

obj = event.srcElement ? event.srcElement : event.target; 

innerText的問題

innerTextIE中能正常工作,但是innerTextFireFox中卻不行。

<p id="element"></p> <script type="text/javascript"> if(navigator.appName.indexOf("Explorer") >-1){ document.getElementById('element').innerText = "my text"; } else{ document.getElementById('element').textContent = "my text"; } </script> 

跨瀏覽器獲取和設置innerText

//跨瀏覽器獲取innerText function getInnerText(element){ return (typeof element.textContent == 'string') ? element.textContent : element.innerText; } //跨瀏覽器設置innerText function setInnerText(element,text){ if(typeof element.textContent == 'string'){ element.textContent = text; }else{ element.innerText = text; } } 

oninput,onpropertychange,onchange的用法

onchange觸發事件必須滿足兩個條件:

a)當前對象屬性改變,並且是由鍵盤或鼠標事件激發的(腳本觸發無效)

b)當前對象失去焦點(onblur);

onpropertychange的話,只要當前對象屬性發生改變,都會觸發事件,但是它是IE專屬的;

oninput是onpropertychange的非IE瀏覽器版本,支持firefox和opera等瀏覽器,但有一點不同,它綁定於對象時,並非該對象所有屬性改變都能觸發事件,它只在對象value值發生改變時奏效。 

訪問XMLHTTPRequest對象

<script type="text/javascript"> if(window.XMLHttpRequest){ xhr = new XMLHttpRequest();//非IE }else if(window.ActiveXObject){ xhr = new ActiveXObject("Microsoft.XMLHttp");//IE } </script> 

禁止選取網頁內容

問題:  
FF需要用CSS禁止,IE用JS禁止  

解決方法:  
IE: obj.onselectstart = function() {return false;} FF: -moz-user-select:none; 

三大不冒泡事件

所有瀏覽器的focus/blur事件都不冒泡,萬幸的是大部分瀏覽器支持focusin/focusout事件,不過可惡的firefox連這個都不支持。

IE6、7、8下 submit事件不冒泡。
IE6、7、8下 change事件要等到blur時才觸發。 

萬惡的滾輪事件

滾輪事件的支持可謂是亂七八糟,規律如下:

IE6-11 chrome mousewheel wheelDetla 下 -120 上 120

firefox DOMMouseScroll detail 下3 上-3

firefox wheel detlaY 下3 上-3

IE9-11 wheel deltaY 下40 上-40

chrome wheel deltaY 下100 上-100  

關於鼠標滾輪事件,IE支持mousewheel,火狐支持DOMMouseScroll
判斷鼠標滾輪是向上還是向下,IE是通過wheelDelta屬性,而火狐是通過detail屬性

事件委托方法

//事件委托方法 IE:document.body.onload = inject; //Function inject()在這之前已被實現 FF:document.body.onload = inject(); 

HTML5 的瀏覽器支持情況

來源地址:http://fmbip.com/litmus/

查詢操作

查詢通過指的是通過一些特征字符串來找到一組元素,或者判斷元素是不是滿足字符串。

1. IE6/7不區分id和nam 在IE6/7下使用getElementById和getElementsByName時會同時返回id或name與給定值相同的元素。由於name通常由后端約定,因此我們在寫JS時,應保證id不與name重復。 2. IE6/7不支持getElementsByClassName和querySelectorAll 這兩個函數從IE8開始支持的,因此在IE6/7下,我們實際可以用的只有getElementByTagName。 3. IE6/7不支持getElementsByTagName('*')會返回非元素節點 要么不用*,要么自己寫個函數過濾一下。 4. IE8下querySelectorAll對屬性選擇器不友好 幾乎所有瀏覽器預定義的屬性都有了問題,盡量使用自定義屬性或者不用屬性選擇器。 5. IE8下querySelectorAll不支持偽類 有時候偽類是很好用,IE8並不支持,jquery提供的:first、:last、:even、:odd、:eq、:nth、:lt、:gt並不是偽類,我們在任何時間都不要使用它們。 6. IE9的matches函數不能處理不在DOM樹上的元素 只要元素不在dom樹上,一定會返回false,實在不行把元素丟在body里面匹配完了再刪掉吧,當然了我們也可以自己寫匹配函數以避免回流。 

轉自:http://segmentfault.com/a/1190000002650240

資料參考:
http://w3help.org/zh-cn/kb/
http://www.zhihu.com/question/29072028


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM