JavaScript操作DOM的那些坑

开发 前端
js在操作DOM中存在着许多跨浏览器方面的坑,本文花了我将近一周的时间整理,我将根据实例整理那些大大小小的“坑”。

js在操作DOM中存在着许多跨浏览器方面的坑,本文花了我将近一周的时间整理,我将根据实例整理那些大大小小的“坑”。

DOM的工作模式是:先加载文档的静态内容、再以动态方式对它们进行刷新,动态刷新不影响文档的静态内容。

PS:IE 中的所有 DOM 对象都是以 COM 对象的形式实现的,这意味着 IE 中的 DOM可能会和其他浏览器有一定的差异。

Node 接口

 

firstChild 相当于 childNodes[0];lastChild 相当于childNodes[box.childNodes.length - 1]。

nodeType返回结点的类型

--元素结点返回1

--属性结点返回2

--文本结点返回3

innerHTML 和 nodeValue

对于文本节点,nodeValue 属性包含文本。

对于属性节点,nodeValue 属性包含属性值。

nodeValue 属性对于文档节点和元素节点是不可用的。

两者区别

  1. box.childNodes[0].nodeValue = '<strong>abc</strong>';//结果为:<strong>abc</strong> 
  2.  
  3. abcbox.innerHTML = '<strong>abc</strong>';//结果为:abc  

nodeName属性获得结点名称

--对于元素结点返回的是标记名称,如:<a herf><a>返回的是"a"

--对于属性结点返回的是属性名称,如:class="test" 返回的是test

--对于文本结点返回的是文本的内容

tagName

document.getElementByTagName(tagName):返回一个数组,包含对这些结点的引用

getElementsByTagName()方法将返回一个对象数组 HTMLCollection(NodeList),这个数组保存着所有相同元素名的节点列表。

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

PS:IE 浏览器在使用通配符的时候,会把文档最开始的 html 的规范声明当作第一个元素节点。

  1. document.getElementsByTagName('li');//获取所有 li 元素,返回数组 
  2.  
  3. document.getElementsByTagName('li')[0];//获取第一个 li 元素,HTMLLIElement 
  4.  
  5. document.getElementsByTagName('li').item(0);//获取第一个 li 元素,HTMLLIElement 
  6.  
  7. 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的文本节点

获取鼠标点击事件的位置

 

以下所描述的属性在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

参照表

(+为支持,-为不支持):

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

getComputedStyle()接受两个参数:要取得计算样式的元素和一个伪元素,如果不需要伪元素,则可以是null。然而,在IE中,并不支持getComputedStyle,IE提供了currentStyle属性。

getComputedStyle(obj , false ) 是支持 w3c (FF12、chrome 14、safari):在FF新版本中只需要第一个参数,即操作对象,第二个参数写“false”也是大家通用的写法,目的是为了兼容老版本的火狐浏览器。

缺点:在标准浏览器中正常,但在IE6/7/8中不支持

  1. window.onload=function(){ 
  2.  
  3.     var oBtn=document.getElementById('btn'); 
  4.  
  5.     var oDiv=document.getElementById('div1'); 
  6.  
  7.   
  8.  
  9.     oBtn.onclick=function(){ 
  10.  
  11.         //alert(oDiv.style.width); //写在样式表里无法读取,只能得到写在行内的 
  12.  
  13.         //alert(getComputedStyle(oDiv).width); //适用于标准浏览器       IE6、7、8不识别 
  14.  
  15.         //alert(oDiv.currentStyle.width); //适用于IE浏览器,标准浏览器不识别 
  16.  
  17.         if(oDiv.currentStyle){ 
  18.  
  19.             alert(oDiv.currentStyle.width); 
  20.  
  21.         }else
  22.  
  23.             alert(getComputedStyle(oDiv).width); 
  24.  
  25.         } 
  26.  
  27.   
  28.  
  29.     }; 
  30.  
  31. };  

取消表单提交

  1. <script type="text/javascript"
  2.  
  3.     function listenEvent(eventObj,event,eventHandler){ 
  4.  
  5.         if(eventObj.addEventListener){ 
  6.  
  7.             eventObj.addEventListener(event,eventHandler,false); 
  8.  
  9.         }else if(eventObj.attachEvent){ 
  10.  
  11.             event = "on" + event; 
  12.  
  13.             eventObj.attachEvent(event,eventHandler); 
  14.  
  15.         }else
  16.  
  17.             eventObj["on" + event] = eventHandler; 
  18.  
  19.         } 
  20.  
  21.     } 
  22.  
  23.   
  24.  
  25.     function cancelEvent(event){ 
  26.  
  27.         if(event.preventDefault){ 
  28.  
  29.             event.preventDefault();//w3c 
  30.  
  31.         }else
  32.  
  33.             event.returnValue = true;//IE 
  34.  
  35.         } 
  36.  
  37.     } 
  38.  
  39.   
  40.  
  41.     window.onload = function () { 
  42.  
  43.         var form = document.forms["picker"]; 
  44.  
  45.         listenEvent(form,"submit",validateFields); 
  46.  
  47.     }; 
  48.  
  49.   
  50.  
  51.     function validateFields(evt){ 
  52.  
  53.         evt = evt ? evt : window.event; 
  54.  
  55.         ... 
  56.  
  57.         if(invalid){ 
  58.  
  59.             cancelEvent(evt); 
  60.  
  61.         } 
  62.  
  63.     } 
  64.  
  65. </script>  

确定浏览器窗口的尺寸

对于主流浏览器来说,比如IE9、Firefox,Chrome和Safari,支持名为innerWidth 和 innerHeight的窗口对象属性,它返回窗口的视口区域,减去任何滚动条的大小。IE不支持innerWidth 和 innerHeight

  1. <script type="text/javascript"
  2.  
  3.     function size(){ 
  4.  
  5.         var w = 0, h=0; 
  6.  
  7.   
  8.  
  9.         if(!window.innerWidth){ 
  10.  
  11.             w = (document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth); 
  12.  
  13.   
  14.  
  15.             h = (document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight); 
  16.  
  17.         }else
  18.  
  19.             w = window.innerWidth; 
  20.  
  21.             h = window.innerHeight; 
  22.  
  23.         } 
  24.  
  25.         return {width:w,height:h}; 
  26.  
  27.     } 
  28.  
  29.   
  30.  
  31.     console.log(size());//Object { width: 1366, height: 633 } 
  32.  
  33. </script>  

实用的 JavaScript 方案(涵盖所有浏览器):

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

对于 IE 6、7、8的方案如下:

  1. document.documentElement.clientHeight 
  2.  
  3. document.documentElement.clientWidth  

或者

  1. document.body.clientHeight 
  2.  
  3. document.body.clientWidth  

Document对象的body属性对应HTML文档的<body>标签。Document对象的documentElement属性则表示 HTML文档的根节点。

attributes 属性

attributes 属性返回该节点的属性节点集合。

  1. document.getElementById('box').attributes//NamedNodeMap 
  2.  
  3. document.getElementById('box').attributes.length;//返回属性节点个数 
  4.  
  5. document.getElementById('box').attributes[0]; //Attr,返回最后一个属性节点 
  6.  
  7. document.getElementById('box').attributes[0].nodeType; //2,节点类型 
  8.  
  9. document.getElementById('box').attributes[0].nodeValue; //属性值 
  10.  
  11. document.getElementById('box').attributes['id']; //Attr,返回属性为 id 的节点 
  12.  
  13. document.getElementById('box').attributes.getNamedItem('id'); //Attr  

setAttribute 和 getAttribute

在IE中是不认识class属性的,需改为className属性,同样,在Firefox中,也是不认识className属性的,Firefox只认识class属性,所以通常做法如下:

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

IE:可以使用获取常规属性的方法来获取自定义属性,也可以使用getAttribute()获取自定义属性

Firefox:只能使用getAttribute()获取自定义属性.

解决方法:统一通过getAttribute()获取自定义属性

  1. document.getElementById('box').getAttribute('id');//获取元素的 id 值 
  2.  
  3. document.getElementById('box').id;//获取元素的 id 值 
  4.  
  5. document.getElementById('box').getAttribute('mydiv');//获取元素的自定义属性值 
  6.  
  7. document.getElementById('box').mydiv//获取元素的自定义属性值, IE 不支持非 
  8.  
  9. document.getElementById('box').getAttribute('class');//获取元素的 class 值,IE 不支持 
  10.  
  11. 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对象

  1. <!doctype html> 
  2.  
  3. <html lang="en"
  4.  
  5. <head> 
  6.  
  7.     <meta charset="UTF-8"
  8.  
  9.     <title>Document</title> 
  10.  
  11.     <style type="text/css"
  12.  
  13.        #drop
  14.  
  15.            width: 300px; 
  16.  
  17.            height: 200px; 
  18.  
  19.            background-color: #ff0000; 
  20.  
  21.            padding: 5px; 
  22.  
  23.            border: 2px solid #000000; 
  24.  
  25.        } 
  26.  
  27.        #item{ 
  28.  
  29.            width: 100px; 
  30.  
  31.            height: 100px; 
  32.  
  33.            background-color: #ffff00; 
  34.  
  35.            padding: 5px; 
  36.  
  37.            margin: 20px; 
  38.  
  39.            border: 1px dashed black; 
  40.  
  41.        } 
  42.  
  43.        *[draggable = true]{ 
  44.  
  45.            -moz-user-select: none; 
  46.  
  47.            -webkit-user-select: none; 
  48.  
  49.            cursormove
  50.  
  51.        } 
  52.  
  53.     </style> 
  54.  
  55. </head> 
  56.  
  57. <body> 
  58.  
  59. <div> 
  60.  
  61.     <p>将金黄色的小方块拖到红色的大方块中,不兼容IE7及以下浏览器,兼容主流浏览器!</p> 
  62.  
  63. </div> 
  64.  
  65. <div id="item" draggable="true"></div> 
  66.  
  67. <div id="drop"></div> 
  68.  
  69. <script type="text/javascript"
  70.  
  71.     function listenEvent(target,type,handler){ 
  72.  
  73.         if(target.addEventListener){//w3c 
  74.  
  75.             target.addEventListener(type,handler,false); 
  76.  
  77.         }else if(target.attachEvent){//IE 
  78.  
  79.             type = "on" + type; 
  80.  
  81.             target.attachEvent(type,handler);//IE 
  82.  
  83.         }else
  84.  
  85.             target["on" + type] = handler; 
  86.  
  87.         } 
  88.  
  89.     } 
  90.  
  91.   
  92.  
  93.     //取消事件 
  94.  
  95.     function cancelEvent(e){ 
  96.  
  97.         if(e.preventDefault){ 
  98.  
  99.             e.preventDefault();//w3c 
  100.  
  101.         }else
  102.  
  103.             e.returnValue = false;//IE 
  104.  
  105.         } 
  106.  
  107.     } 
  108.  
  109.     //取消传递 
  110.  
  111.     function cancelPropagation(e){ 
  112.  
  113.         if(e.stopPropagation){ 
  114.  
  115.             e.stopPropagation();//w3c 
  116.  
  117.         }else
  118.  
  119.             e.cancelBubble = true;//IE 
  120.  
  121.         } 
  122.  
  123.     } 
  124.  
  125.   
  126.  
  127.     window.onload = function () { 
  128.  
  129.         var target = document.getElementById('drop'); 
  130.  
  131.         listenEvent(target,'dragenter',cancelEvent); 
  132.  
  133.         listenEvent(target,"dragover",dragOver); 
  134.  
  135.         listenEvent(target,'drop'function (evt) { 
  136.  
  137.             cancelPropagation(evt); 
  138.  
  139.             evt = evt || window.event; 
  140.  
  141.             evt.dataTransfer.dropEffect = 'copy'
  142.  
  143.             var id = evt.dataTransfer.getData('Text'); 
  144.  
  145.             target.appendChild(document.getElementById(id)); 
  146.  
  147.         }); 
  148.  
  149.   
  150.  
  151.         var item = document.getElementById('item'); 
  152.  
  153.         item.setAttribute("draggable",'true'); 
  154.  
  155.         listenEvent(item,'dragstart'function (evt) { 
  156.  
  157.             evt = evt || window.event; 
  158.  
  159.             evt.dataTransfer.effectAllowed = 'copy'
  160.  
  161.             evt.dataTransfer.setData('Text',item.id); 
  162.  
  163.         }); 
  164.  
  165.     }; 
  166.  
  167.   
  168.  
  169.     function dragOver(evt){ 
  170.  
  171.         if(evt.preventDefault) evt.preventDefault(); 
  172.  
  173.         evt = evt || window.event; 
  174.  
  175.         evt.dataTransfer.dropEffect = 'copy'
  176.  
  177.         return false
  178.  
  179.     } 
  180.  
  181. </script> 
  182.  
  183. </body> 
  184.  
  185. </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 :

  1. <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

跨浏览器获取目标对象

  1. //跨浏览器获取目标对象 
  2.  
  3. function getTarget(ev){ 
  4.  
  5.     if(ev.target){//w3c 
  6.  
  7.         return ev.target; 
  8.  
  9.     }else if(window.event.srcElement){//IE 
  10.  
  11.         return window.event.srcElement; 
  12.  
  13.     } 
  14.  
  15.  

对于获取触发事件的对象,w3c和IE也有不同的做法:

  1. event.target;//w3c 
  2.  
  3. event.srcElement;//IE  

我们可以使用三目运算符来兼容他们:

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

innerText的问题

innerText在IE中能正常工作,但是innerText在FireFox中却不行。

  1. <p id="element"></p> 
  2.  
  3. <script type="text/javascript"
  4.  
  5.     if(navigator.appName.indexOf("Explorer") >-1){ 
  6.  
  7.         document.getElementById('element').innerText = "my text"
  8.  
  9.     } else
  10.  
  11.         document.getElementById('element').textContent = "my text"
  12.  
  13.     } 
  14.  
  15. </script>  

跨浏览器获取和设置innerText

  1. //跨浏览器获取innerText 
  2.  
  3. function getInnerText(element){ 
  4.  
  5.     return (typeof element.textContent == 'string') ? element.textContent : element.innerText; 
  6.  
  7.  
  8. //跨浏览器设置innerText 
  9.  
  10. function setInnerText(element,text){ 
  11.  
  12.     if(typeof element.textContent == 'string'){ 
  13.  
  14.         element.textContent = text; 
  15.  
  16.     }else
  17.  
  18.         element.innerText = text; 
  19.  
  20.     } 
  21.  
  22.  

oninput,onpropertychange,onchange的用法

onchange触发事件必须满足两个条件:

a)当前对象属性改变,并且是由键盘或鼠标事件激发的(脚本触发无效)

b)当前对象失去焦点(onblur);

onpropertychange的话,只要当前对象属性发生改变,都会触发事件,但是它是IE专属的;

oninput是onpropertychange的非IE浏览器版本,支持firefox和opera等浏览器,但有一点不同,它绑定于对象时,并非该对象所有属性改变都能触发事件,它只在对象value值发生改变时奏效。

访问XMLHTTPRequest对象

  1. <script type="text/javascript"
  2.  
  3.     if(window.XMLHttpRequest){ 
  4.  
  5.         xhr = new XMLHttpRequest();//非IE 
  6.  
  7.     }else if(window.ActiveXObject){ 
  8.  
  9.         xhr = new ActiveXObject("Microsoft.XMLHttp");//IE 
  10.  
  11.     } 
  12.  
  13. </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属性

事件委托方法 

  1. //事件委托方法 
  2.  
  3. IE:document.body.onload = inject; //Function inject()在这之前已被实现 
  4.  
  5. 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://w3help.org/zh-cn/kb/,

http://www.zhihu.com/question/29072028 

责任编辑:庞桂玉 来源: 前端大全
相关推荐

2010-09-13 17:12:55

JavaScript

2010-09-08 15:47:08

JavsScriptJavaScript

2010-09-28 14:52:57

JavaScriptDOM

2010-09-08 16:50:11

JavaScriptDOM操作

2016-04-06 11:29:58

JavaScriptDOM操作

2010-09-28 15:07:48

JavaScript

2020-04-21 15:18:11

财务信息化

2015-03-12 09:51:09

CoreDataiCloud

2021-09-07 14:35:48

DevSecOps开源项目

2022-05-15 08:13:50

Mysql数据库Mycat

2021-08-26 10:25:04

JavaScript进阶操作 前端

2021-08-30 10:25:48

JavaScript进阶操作前端

2022-07-06 07:42:14

DOMHook标签

2019-12-11 09:23:51

JavaScriptHTMLXML

2010-09-08 09:59:46

JavaScriptDOM操作

2010-09-28 15:12:27

Javascript

2010-09-08 17:26:46

JavaScript

2013-04-12 15:59:33

2020-05-28 16:15:50

HTTP暗坑前端

2017-07-06 11:41:48

CIOIT技术
点赞
收藏

51CTO技术栈公众号