再谈 BOM 和 DOM(5): 各个大流浪器 DOM 和 BOM 里面的那些坑—兼容性
三大不冒泡事件所有浏览器的 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 属性
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 返回选定节点的上一个同级节点,若不存在,则返回 nullnextSibling Node 返回被选节点的下一个同级节点,若不存在,则返回 nullhasChildNodes() Boolean 如果当前元素节点拥有子节点,返回 true,否则返回 falseattributes NamedNodeMap 返回包含被选节点属性的 NamedNodeMapappendChild(node) node 将 node 添加到 childNodes 的末尾 removeChild(node) node 从 childNodes 中删除 nodereplaceChild(newnode, oldnode) Node 将 childNodes 中的 oldnode 替换成 newnodeinsertBefore Node 在已有子节点之前插入新的子节点 firstChild 相当于 childNodes[0];lastChild 相当于 childNodes[box.childNodes.length - 1]。
nodeType 返回结点的类型--元素结点返回 1--属性结点返回 2--文本结点返回 3
innerHTML 和 nodeValue 对于文本节点,nodeValue 属性包含文本。
对于属性节点,nodeValue 属性包含属性值。
nodeValue 属性对于文档节点和元素节点是不可用的。
两者区别
box.childNodes[0].nodeValue = 'abc';//结果为:abcabcbox.innerHTML = 'abc';//结果为:abcnodeName 属性获得结点名称--对于元素结点返回的是标记名称,如:返回的是"a"--对于属性结点返回的是属性名称,如:class="test" 返回的是 test
--对于文本结点返回的是文本的内容
tagNamedocument.getElementByTagName(tagName):返回一个数组,包含对这些结点的引用 getElementsByTagName()方法将返回一个对象数组 HTMLCollection(NodeList),这个数组保存着所有相同元素名的节点列表。
document.getElementsByTagName('*');//获取所有元素 PS:IE 浏览器在使用通配符的时候,会把文档最开始的 html 的规范声明当作第一个元素节点。
document.getElementsByTagName('li');//获取所有 li 元素,返回数组 document.getElementsByTagName('li')[0];//获取第一个 li 元素,HTMLLIElementdocument.getElementsByTagName('li').item(0);//获取第一个 li 元素,HTMLLIElementdocument.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 = mouseClickfunction mouseClick (ev) {ev = ev || window.event//window.event 用来兼容 IEvar x = 0var y = 0if (ev.pageX) {x = ev.pageXy = ev.pageY} else if (ev.clientX) {var offsetX = 0, offsetY = 0if (document.documentElement.scrollLeft) {offsetX = document.documentElement.scrollLeftoffsetY = document.documentElement.scrollTop} else if (document.body) {offsetX = document.body.scrollLeftoffsetY = document.body.scrollTop}x = ev.clientX + offsetXy = ev.clientY + offsetY}alert('你点击的位置是 x=' + x + ' y=' + y)}以下所描述的属性在 chrome 和 Safari 都很给力的支持了。
问题一:Firefox,Chrome、Safari 和 IE9 都是通过非标准事件的 pageX 和 pageY 属性来获取 web 页面的鼠标位置的。pageX/Y 获取到的是触发点相对文档区域左上角距离,以页面为参考点,不随滑动条移动而变化
问题二:在 IE 中,event 对象有 x, y 属性(事件发生的位置的 x 坐标和 y 坐标)火狐中没有。在火狐中,与 event.x 等效的是 event.pageX。event.clientX 与 event.pageX 有微妙的差别(当整个页面有滚动条的时候),不过大多数时候是等效的。
offsetX:IE 特有,chrome 也支持。鼠标相比较于触发事件的元素的位置,以元素盒子模型的内容区域的左上角为参考点,如果有 boder,可能出现负值
问题三:scrollTop 为滚动条向下移动的距离,所有浏览器都支持 document.documentElement。
其余参照:http://segmentfault.com/a/1190000002559158#articleHeader11
参照表查看下方 DEMO:
https://jsfiddle.net/f4am208m/embedded/result/
你会发现 offsetX 在 Firefox 下是 undefined,在 chrome 和 IE 则会正常显示。
dom event 元素位置图片
offsetLeft 和 style.left 区别 style.left 返回的是字符串,比如 10px。而 offsetLeft 返回的是数值,比如数值 10
style.left 是可读写的,offsetLeft 是只读的
style.left 的值需要事先定义(在样式表中定义无效,只能取到在 html 中定义的值),否则取到的值是空的
getComputedStyle 与 currentStylegetComputedStyle()接受两个参数:要取得计算样式的元素和一个伪元素,如果不需要伪元素,则可以是 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)}
}}取消表单提交 function listenEvent (eventObj, event, eventHandler) {if (eventObj.addEventListener) {eventObj.addEventListener(event, eventHandler, false)} else if (eventObj.attachEvent) {event = 'on' + eventeventObj.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)}}确定浏览器窗口的尺寸对于主流浏览器来说,比如 IE9、Firefox,Chrome 和 Safari,支持名为 innerWidth 和 innerHeight 的窗口对象属性,它返回窗口的视口区域,减去任何滚动条的大小。IE 不支持 innerWidth 和 innerHeight
function size () {var w = 0, h = 0if (!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.innerWidthh = window.innerHeight}return { width: w, height: h }}
console.log(size())//Object { width: 1366, height: 633 }实用的 JavaScript 方案(涵盖所有浏览器):
var w = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidthvar h = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight 对于 IE 6、7、8 的方案如下:
document.documentElement.clientHeightdocument.documentElement.clientWidth 或者
document.body.clientHeightdocument.body.clientWidthDocument 对象的 body 属性对应 HTML 文档的标签。Document 对象的 documentElement 属性则表示 HTML 文档的根节点。
attributes 属性 attributes 属性返回该节点的属性节点集合。
document.getElementById('box').attributes//NamedNodeMap/ndocument.getElementById('box').attributes.length//返回属性节点个数/ndocument.getElementById('box').attributes[0] //Attr,返回最后一个属性节点/ndocument.getElementById('box').attributes[0].nodeType //2,节点类型/ndocument.getElementById('box').attributes[0].nodeValue //属性值/ndocument.getElementById('box').attributes['id'] //Attr,返回属性为 id 的节点/ndocument.getElementById('box').attributes.getNamedItem('id') //AttrsetAttribute 和 getAttribute 在 IE 中是不认识 class 属性的,需改为 className 属性,同样,在 Firefox 中,也是不认识 className 属性的,Firefox 只认识 class 属性,所以通常做法如下:
element.setAttribute(class, value); //for firefoxelement.setAttribute(className, value); //for IEIE:可以使用获取常规属性的方法来获取自定义属性,也可以使用 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()方法设置 class 和 style 属性是没有效果的,虽然 IE8 解决了这个 bug,但还是不建议使用。
removeAttribute()方法 removeAttribute()可以移除 HTML 属性。
document.getElementById('box').removeAttribute('style');//移除属性 PS:IE6 及更低版本不支持 removeAttribute()方法。
dataTransfer 对象
HTML5 拖拽的浏览器支持 Internet Explorer 9、Firefox、Opera 12、Chrome 以及 Safari 5 支持拖放
为了使元素可拖动,需把 draggable 属性设置为 true :
<div draggable="true"></div>
上述代码的一些浏览器兼容性:
为了兼容 IE,我们将window.event
赋给 evt
,其他浏览器则会正确将接收到的event
对象赋给evt
。
w3c 使用 addEventListener 来为事件元素添加事件监听器,而 IE 则使用 attachEvent。addEventListener 为事件冒泡到的当前对象,而 attachEvent 是 window
对于事件类型,IE 需要加on + type
属性,而其他浏览器则不用 4.对于阻止元素的默认事件行为,下面是 w3c 和 IE 的做法:
e.preventDefault();//w3c
returnValue = false;//IE
对于取消事件传播,w3c 和 IE 也有不同的处理机制:
e.stopPropagation();//w3c
e.cancelBubble = true;//IE
跨浏览器获取目标对象//跨浏览器获取目标对象 function getTarget (ev) {if (ev.target) {//w3creturn ev.target} else if (window.event.srcElement) {//IEreturn window.event.srcElement}}对于获取触发事件的对象,w3c 和 IE 也有不同的做法:
event.target;//w3cevent.srcElement;//IE 我们可以使用三目运算符来兼容他们:
obj = event.srcElement ? event.srcElement : event.target;innerText 的问题 innerText 在 IE 中能正常工作,但是 innerText 在 FireFox 中却不行。
if (navigator.appName.indexOf('Explorer') > -1) {document.getElementById('element').innerText = 'my text'} else {document.getElementById('element').textContent = 'my text'}跨浏览器获取和设置 innerText//跨浏览器获取 innerTextfunction getInnerText (element) {return (typeof element.textContent == 'string') ? element.textContent : element.innerText}
//跨浏览器设置 innerTextfunction setInnerText (element, text) {if (typeof element.textContent == 'string') {element.textContent = text} else {element.innerText = text}}oninput,onpropertychange,onchange 的用法 onchange 触发事件必须满足两个条件:
当前对象属性改变,并且是由键盘或鼠标事件激发的(脚本触发无效)
当前对象失去焦点(onblur);
onpropertychange 的话,只要当前对象属性发生改变,都会触发事件,但是它是 IE 专属的;
oninput 是 onpropertychange 的非 IE 浏览器版本,支持 firefox 和 opera 等浏览器,但有一点不同,它绑定于对象时,并非该对象所有属性改变都能触发事件,它只在对象 value 值发生改变时奏效。
访问 XMLHTTPRequest 对象 if(window.XMLHttpRequest){xhr = new XMLHttpRequest();//非 IE}else if(window.ActiveXObject){xhr = new ActiveXObject("Microsoft.XMLHttp");//IE}禁止选取网页内容问题:
FF 需要用 CSS 禁止,IE 用 JS 禁止
解决方法:
IE: obj.onselectstart = function() {return false;}
FF: -moz-user-select:none;
事件委托方法//事件委托方法
IE:document.body.onload = inject;FF:document.body.onload = inject();Function inject(){}HTML5 的浏览器支持情况 HTML5 的浏览器支持情况来源地址:http://fmbip.com/litmus/
查询操作查询通过指的是通过一些特征字符串来找到一组元素,或者判断元素是不是满足字符串。
IE6/7 不区分 id 和 nam 在 IE6/7 下使用 getElementById 和 getElementsByName 时会同时返回 id 或 name 与给定值相同的元素。由于 name 通常由后端约定,因此我们在写 JS 时,应保证 id 不与 name 重复。
IE6/7 不支持 getElementsByClassName 和 querySelectorAll 这两个函数从 IE8 开始支持的,因此在 IE6/7 下,我们实际可以用的只有 getElementByTagName。
IE6/7 不支持 getElementsByTagName('')会返回非元素节点要么不用,要么自己写个函数过滤一下。
IE8 下 querySelectorAll 对属性选择器不友好几乎所有浏览器预定义的属性都有了问题,尽量使用自定义属性或者不用属性选择器。
IE8 下 querySelectorAll 不支持伪类有时候伪类是很好用,IE8 并不支持,jquery 提供的:first、:last、:even、:odd、:eq、:nth、:lt、:gt 并不是伪类,我们在任何时间都不要使用它们。
IE9 的 matches 函数不能处理不在 DOM 树上的元素只要元素不在 dom 树上,一定会返回 false,实在不行把元素丢在 body 里面匹配完了再删掉吧,当然了我们也可以自己写匹配函数以避免回流。
资料参考:
http://w3help.org/zh-cn/kb/,http://www.zhihu.com/question/29072028
转载本站文章《再谈 BOM 和 DOM(5):各个大流浪器 DOM 和 BOM 里面的那些坑—兼容性》,请注明出处:https://www.zhoulujun.cn/html/webfront/SGML/xml/2016_0406_7740.html
版权声明: 本文为 InfoQ 作者【zhoulujun】的原创文章。
原文链接:【http://xie.infoq.cn/article/47a5daece00505027b0aa14f4】。文章转载请联系作者。
评论