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
innerhtml 和 nodevalue
对于文本节点,nodevalue 属性包含文本。 对于属性节点,nodevalue 属性包含属性值。 nodevalue 属性对于文档节点和元素节点是不可用的。
两者区别
box.childnodes[0].nodevalue = 'abc';//结果为:abc abcbox.innerhtml = 'abc';//结果为:abc
nodename属性获得结点名称
--对于元素结点返回的是标记名称,如:返回的是"a" --对于属性结点返回的是属性名称,如:class="test" 返回的是test --对于文本结点返回的是文本的内容
tagname
document.getelementbytagname(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); }
以下所描述的属性在chrome和safari 都很给力的支持了。
offsetx:ie特有,chrome也支持。鼠标相比较于触发事件的元素的位置,以元素盒子模型的内容区域的左上角为参考点,如果有boder,可能出现负值
问题三:
scrolltop为滚动条向下移动的距离,所有浏览器都支持document.documentelement。
其余参照:https://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:
你会发现offsetx在firefox下是undefined,在chrome和ie则会正常显示。
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
window.onload=function(){ var obtn=document.getelementbyid('btn'); var odiv=document.getelementbyid('p1'); 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>
确定浏览器窗口的尺寸
<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>
var w=window.innerwidth || document.documentelement.clientwidth || document.body.clientwidth; var h=window.innerheight || document.documentelement.clientheight|| document.body.clientheight;
document.documentelement.clientheight document.documentelement.clientwidth
document.body.clientheight document.body.clientwidth标签。document对象的documentelement属性则表示 html文档的根节点。
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('myp');//获取元素的自定义属性值 document.getelementbyid('box').myp//获取元素的自定义属性值, 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()方法。
跨浏览器事件event对象
将金黄色的小方块拖到红色的大方块中,不兼容ie7及以下浏览器,兼容主流浏览器!
<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>
datatransfer 对象
属性 | 描述 |
---|---|
dropeffect | 设置或获取拖曳操作的类型和要显示的光标类型 |
effectallowed | 设置或获取数据传送操作可应用于该对象的源元素 |
方法 | 描述 |
---|---|
cleardata | 通过 datatransfer 或 clipboarddata 对象从剪贴板删除一种或多种数据格式 |
getdata | 通过 datatransfer 或 clipboarddata 对象从剪贴板获取指定格式的数据 |
setdata | 以指定格式给 datatransfer 或 clipboarddata 对象赋予数据 |
html5拖拽的浏览器支持
internet explorer 9、firefox、opera 12、chrome 以及 safari 5 支持拖放
为了使元素可拖动,需把 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; } }
event.target;//w3c event.srcelement;//ie
obj = event.srcelement ? event.srcelement : event.target;
innertext的问题
innertext在ie中能正常工作,但是innertext在firefox中却不行。
<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的用法
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 的浏览器支持情况
查询操作
查询通过指的是通过一些特征字符串来找到一组元素,或者判断元素是不是满足字符串。
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里面匹配完了再删掉吧,当然了我们也可以自己写匹配函数以避免回流。
上一篇: 清炖羊排萝卜汤的做法是什么