dom 操作
结构图
结构说明:
- Document表示一个HTML或者XML文档, Element表示文档中的一个元素
- HTMLDocument和HTMLElement为html文档和元素的数据类型
select文档元素:
- Document.getElementById(): IE8以前的版本执行匹配时不区分大小写并且返回name属性匹配的元素
- HTMLDocument.getElementsByName(): 返回NodeList类似数组的只读对象. IE中会返回id匹配的元素.
- Document.getElementsByTagName(): 返回NodeList, 元素顺序为文档中出现的顺序, getElementsByTagName(*)返回文档中所有元素.
- Element.getElementsByTagName(): 只选择符合要求的子孙节点
- 由于历史原因HTMLDocument定义了images, forms和links等属性用于快速访问节点, 这些属性是HTMLCollection类型, 与NodeList相似但是支持以id和name进行索引如: document.forms.shipping_address访问name或者id为shipping_address的form元素
- getElementByName(), getElementsByTagName()返回的NodeList与document.images的HTMLCollection有如下需要注意的:
- 它们都是类似数组的只读对象, 有length属性可用于访问, 索引, 迭代
- 不能直接对它们调用Array方法, 可以间接调用: var content = Array.prototype.map.call(document.getElementsByTagName("p"), function (e) { return e.innerHTML; });
- HTMLCollection可以通过属性id或者那么访问
- NodeList和HTMLCollection同时也是函数, 传入索引参数返回对应元素, 但是不建议调用
- 它们都是动态反应文档结构内容, 对dom的修改会实时反应到它们结构中. 如需要访问静态结构可以这样:var snapshot = Array.prototype.slice.call(nodelist, 0);
- HTMLDocument.getElementsByClassName(), HTMLElement.getElementsByClassName()返回包含匹配元素的动态NodeList. 参数为字符串, 可以是多个空格分隔的class值, 当所有class都得到匹配时才认为一个元素匹配, 在怪癖模式下匹配不区分大小写, 标准模式下区分大小写. IE8及以前版本不支持getElementsByClassName()
- Document.querySelectorAll(), Element.querySelectorAll(): 参数为合法的css选择器字符串, 返回代表匹配元素的静态NodeList. 如果没有匹配元素则返回空NodeList, 如果选择器字符串非法, 抛出异常.
- Document.querySelector(), Element.querySelector(): 返回第一个匹配的元素, 如果不匹配返回null
- QuerySelectorAll()和querySelector()不会匹配类似:first-line, :first-letter, :link, :visited等选择器. 当前主流浏览器实现了这两个方法, 支持css3选择器. IE9之前的版本不支持css3选择器
文档遍历
- Document as trees of Nodes: Node定义如下属性:
- parentNode: 当前节点的父节点, 如无父节点(文档根节点), 为null
- childNodes: 只读类数组NodeList对象, 包含所有子节点
- firstChild, lastChild: 节点的第一和最后一个节点, 如果没有子节点为null
- nextSibling, previousSibling: 当前节点的前一个和后一个兄弟节点, 文档中出现先后确定顺序
- nodeType: Document节点值为9, Element节点值为1, Text节点值为3, Comment节点值为8, DocumentFragment节点为11
- nodeValue: Text或者Comment节点的文本内容
- nodeName: 元素的标签名, 大写. 如<a>节点node.nodeName === "A"
- 注: 空白, 换行等字符对此类型的api有影响
- Document As tree of Element: Element定义如下属性:
- children: NodeList包含元素的所有子元素, 不是标准属性但是所有浏览器都支持
- firstElementChild, lastElementChild: 子元素, IE不支持
- nextElementSibling, previousElementSibling: 前一个, 后一个兄弟元素, IE不支持
- childElementCount: 子元素个数, 与children.length相同 IE不支持
通用的遍历方法: - parent(e, n):
1 function parent(e, n) 2 { 3 if (n === undefined) { n = 1; } 4 while (n-- && e) 5 { 6 e = e.parentNode; 7 } 8 if (!e || e.nodeType !== 1) { return null; } 9 return e; 10 } // end parent()
- sibling(e, n)
1 function sibling(e, n) 2 { 3 while (n && e) 4 { 5 if (n > 0) 6 { 7 e = e.nextSibling; 8 if (e.nodeType !== 1){ continue; } // end if 9 else { n--; } // end else 10 } // end if 11 else 12 { 13 e = e.previouseSibling; 14 if (e.nodetype !== 1) { continue; } // end if 15 else { n++; } // end else 16 } // end else 17 } // end while 18 return e; 19 } // end sibling()
- child
1 function child(e, n) 2 { 3 if (!e) { return null; } // end if 4 if (e.children) 5 { 6 if (n < 0) { n += e.children.length; } // end if 7 if (n < 0) { return null; } // end if 8 return e.children[n]; 9 } // end if 10 11 if (n > 0) 12 { 13 if (e.firstElementChild) { e = e.firstElementChild; } // end if 14 else 15 { 16 for (e = e.firstChild; e && e.nodeType !== 1; e = e.nextSibling) { /* empty */ } // end for 17 } // end else 18 return sibling(e, n - 1); 19 } // end if 20 else 21 { 22 if (e.lastElementChild) { e = e.lastElementChild; } // end if 23 else 24 { 25 for (e = e.lastChild; e && e.nodeType !== 1; e = e.previousSibling) { /* empty */ } // end for 26 } // end else 27 return sibling(e, n + 1); 28 } // end else 29 return e; 30 } // end child()
属性读写
- HTML attributes as Element properties: HTMLElement对象定义与全局属性对应的property, 特定的子类为其原始定义特有的property. 例如
1 var f = document.forms[0]; 2 f.action = "http:///www.example.com/submit.php"; 3 f.method = "POST";
HTMl属性不区分大小写, 但是javascript对应property区分, 所有的属性都转换为小写, 多个单词使用驼峰标识如: defaultChecked, tabIndex等, 对于某些属性是javascript中的关键字, 保留字, 通常加上html签字如<label>的for属性对应javascript中为htmlFor, class属性对应为className, property通常为string类型, 也有对应boolean或者numeric类型, 如defaultChecked, maxLength等, 事件句柄属性未对象函数. 需要注意style属性类型为CSSStyleDeclaration, 这种属性访问方法没有提供删除属性的功能.
- Getting and setting non-html attributes: Element.getAttribute(), Element.setAttribute()可以用来查询, 设置非HTML标准属性以及XML属性如:
1 var image = document.images[0]; 2 var width = parseInt(image.getAttribute("width")); 3 image.setAttribute("class", "thumbnail");
需要注意: 1, 这种方法得到的值均为string. 2, javascript关键字, 保留字均可直接使用, 函数执行匹配时不区分大小写. Element.hasAttribute(), Element.removeAttribute()用于判断属性是否设置和移除属性, 对于boolean类型的属性尤其重要. XML版本中需要维护namespace时可以使用对于的namespace版本: getAttributeNs(), setAttribute(), hasAttributeNS(), removeAttributeNS()
- Dataset Attribute: HTML5允许用户自定义属性, 当属性以data-作为前缀时自定义属性可以通过html5校验, HTML5为Element定义了dataset属性用于保存对于自定义属性值, 此时将去掉data-前缀, 如data-x对应为dataset.x, 使用-分隔的属性转换为驼峰标识如: data-jquery-test对应dataset.jqueryTest
- Attributes as attr Nodes: Node定义只读类数组属性attributes, 当元素不是Element类型时次属性为null
元素内容:
- Element Content As html: 通过e.innerHTML获取, 设置元素内html代码的内容, 设置这个属性会促发浏览器自动解析并替换原有节点, 这样通常efficient, 这个方法同样可以用于XML元素, html5同样标注化了outerHTML返回包含元素标签的文档内容, insertAdjacentHTML(pos, markup)由IE引入并由HTML5规范化, 允许在元素附近插入元素
- Element Content as plain text: e.textContent, IE外所有浏览器都支持, IE使用innerText
- Element content as text node: 参考前面的文档遍历部分
creating inserting and deleting node
- Document.createElement()标签名作为参数, 对HTML文档不区分大小写, XML文档区分大小写
- Document.createTextNode(), Document.createComment(), Document.createDocumentFragment(), Document.createElementNS()
- Node.cloneNode(), 参数为true执行完全copy, false执行浅copy
- Node.appendChild(e): 将e元素插入到调用元素的末尾, 成为lastChild
- Node.insertBefore(e, posE): 调用者为父节点posE为需要在前插入元素的位置元素posE为null时与appendChild()相同效果, 两个方法将已经存在文档中的元素插入到新元素时会将其从原有位置删除,不用手动删除
1 function sortrows(table, n, comparator) 2 { 3 var tbody = table.tBodies[0]; 4 var rows = tbody.getElementsByTagName("tr"); 5 rows = Array.prototype.slice.call(rows, 0); 6 7 8 rows.sort(function (row1, row2){ 9 var cell1 = row1.getElementsByTagName("td")[n]; 10 var cell2 = row2.getElementsByTagName("td")[n]; 11 var val1 = cell1.textContent || cell1.innerText; 12 var val2 = cell2.textContent || cell2.innerText; 13 if (comparator) 14 { 15 return comparator(val1, val2); 16 } // end if 17 if (val1 < val2) 18 { 19 return -1; 20 } // end if 21 else if (val1 > val2) 22 { 23 return 1; 24 } // end if 25 else 26 { 27 return 0; 28 } // end else 29 }); 30 31 for (var i = 0; i < rows.length; ++i) 32 { 33 tbody.appendChild(rows[i]); 34 } // end for 35 } // end sortrows() 36 37 function makesortable(table) 38 { 39 var headers = table.getElementsByTagName("thead")[0].getElementsbyTagName("th"); 40 for (var i = 0; i < headers.length; ++i) 41 { 42 (function (n){ 43 headers[i].onclick = function () { sortrows(table, n); }; 44 }(i)); 45 } // end for 46 47 } // end makesortable()
- Element.remveChild(): 父节点调用,传入需要删除的子节点自删除: e.parentNode.removeChild(e);
- Element.replaceChild(newChild, old): 父节点调用.用newChild替代old
获取元素几何信息:
- viewport coordinate: 显示文档到桌面部分的坐标系, x轴范围[0, 客户区宽度], y轴范围[0, 客户区高度]
- document coordinate: 整个文档的坐标系, x轴范围[0, 无限], y轴范围[0, 无限]
- 两种坐标系转换: document coordinate = viewport coordinate + scrollbarOffset
- scrollbar位置: window对象的pageXOffset, pageYOffset在大部分浏览器中可以获取滚动条位置, IE8及更早的版本不支持. IE以及所有浏览器都支持通过scrollLeft和scrollTop获取信息. 标准模式下通过查询文档的根节点元素(document.documentElement), 但是在怪癖模式下需要查询<body>元素
1 function getScrollOffsets(w) 2 { 3 w = w || window; 4 // works for all browsers except IE 8 and before 5 if (w.pageXOffset != null) 6 { 7 return {x: w.pageXOffset, y: w.pageYOffset; }; 8 } // end if 9 10 var d = w.document; 11 // for IE (or any browser) in standards mode 12 if (document.compatMode == "CSS1Compat") 13 { 14 return {x: d.documentElement.scrollLeft, y: d.documentElement.scrollTop }; 15 } // end if 16 17 // quirks mode 18 return {x: d.body.scrollLeft, y: d.body.scrollTop }; 19 } // end getScrollOffsets()
- 获取viewport size
1 function getViewportSize(w) 2 { 3 w = w || window; 4 // this works for all browser except ie8 and before 5 if (w.innerWidth != null) 6 { 7 return {w: w.innerWidth, h: w.innerHeight; }; 8 } // end if 9 10 var d = w.document; 11 // for ie (or any browser) in standards mode 12 if (document.compatMode == "CSS1Compat") 13 { 14 return {w: d.documentElement.clientWidth, 15 h: d.documentElement.clientHeight}; 16 } // end if 17 18 // for browser in quirks mode 19 return {w: d.body.clientWidth, h: d.body.clientHeight }; 20 21 } // end getViewportSize()
- e.getBoundingClientRect(): 所有浏览器都支持, 返回对象包含left, right, top, bottom属性分别以viewport coordinate计算得到元素四个方向值, 这四个值包含border和padding但不包含margin部分. 对于block类型元素这个方法工作正常, 对于跨行的inline元素如<span>, <code>,<b>则需要注意, 对于这些元素需要调用getClientRects()得到只读的类数组对象, 其元素与getBoundingClientRect()相同, 两个方法返回的对象都为调用函数时的状态,不会实时更新.
- Document.elementFromPoint(x, y): 以viewport coordinate传入坐标, 返回当前位于这个点的元素: innermost并且uppermost, 如果点不在viewport中.返回null.
- scrollLeft和scrollTop可以查询并通过设置使得浏览器滚动条滚动, Window.scrollT(x, y), Window.scroll(x, y), 传入document coordinate点坐标, 设置滚动条偏移量, 浏览器将这个点尽量放到viewport的0, 0坐标处
1 function toTop() 2 { 3 window.scroll(0, 0); 4 } 5 function toBottom() 6 { 7 var documentHeight = document.documentElement.offsetHeight; 8 var viewportHeight = window.innerHeight; 9 window.scroll(0, documentHeight - viewportHeight); 10 }
- Window.scrollBy(offsetX, offsetY): 在当前位置相对移动滚动条