DOM详解(一)


参考资料

  • 《JavaScript高级教程》
  • 《高性能JavaScript》

节点层次

Node类型及对应数值
  • Node.ELEMENT_NODE(1)
  • Node.ATTRIBUTE_NODE(2)
  • Node.TEXT_NODE(3)
  • Node.CDATA_SECTION_NODE(4)
  • Node.ENTITY_REFERENCE_NODE(5)
  • Node.ENTITY_NODE(6)
  • Node.PROCESSING_INSTRUCTION_NODE(7)
  • Node.COMMENT_NODE(8)
  • Node.DOCUMENT_NODE(9)
  • Node.DOCUMENT_TYPE_NODE(10)
  • Node.DOCUMENT_FRAGMENT_NODE(11)
  • Node.NOTATION_NODE(12)
判断节点类型
if(someNode.nodeType == Node.ELEMENT_NODE) {
  console.log("Node is an elemnt"); // IE中无效
}
if(someNode.nodeType == 1) {
  console.log("Node is an elemnt");//适用所有浏览器
}
childNodes属性

每个节点都有一个childNodes属性,其中保存着一个NodeList对象。NodeList是一种类数组对象,用来保存一组有序的节点,可以通过位置来访问这些节点。

var firstChild = someNode.childNodes[0];
var secondChild = someNode.childNodes.item(1);
var count = someNode.childNodes.length;

可以将NodeList转化为数组(兼容所有浏览器)

function convertToArray(nodes) {
  var array = null;
  try {
    array = Array.prototype.slice.call(nodes, 0);
  } catch (e) {
    array = new Array();
    for (var i = 0; i < nodes.length; i++) {
      array.push(nodes[i]);
    }
  }
  return array;
}
其他节点关系
  • parentNode
  • previousSibling
  • nextSibling
  • firstChild
  • lastChild
  • ownerDocument
操作节点
  • appendChild(someNode):如果是新节点则向childNodes列表的末尾添加一个节点;如果节点已经是文档的一部分,则将节点从原来位置移动到新位置
  • insertBefore(newNode, someNode):两个参数:要插入的节点和作为参照的节点;插入节点后,被插入的节点会变成参照节点的前一个同胞节点;如果参照节点是null则与appendChild执行同样的操作
  • replaceChild(newNode, someNode):两个参数:要插入的节点和要替换的节点
  • removeChild(someNode):移除子节点
  • cloneNode(deep):参数:是否执行深度复制;用于创建调用这个方法的节点的一个完全相同的副本
  • normalize():处理文档树中的文本节点,合并相邻的文本节点并删除空的文本节点
Document类型
  • 特征
    • nodeType = 9
    • nodeName = “#document”
    • nodeValue = null
    • parentNode = null
    • ownerDocument = null
    • 浏览器中是HTMLDocument(继承自Document类型)的一个实例
    • 是window对象的一个属性,可以将其作为全局对象来访问
  • 属性
    • documentElement:直接访问html元素
    • body:取得对body的引用
    • title:取得文档标题
    • URL:取得完整URL
    • domain:取得域名,由于跨域安全限制,来自不同子域的页面无法通过JavaScript通信,而通过将每个页面的document.domain设置为相同的值,这些页面就可以互相访问对方包含的JavaScript对象
    • referrer:取得来源页面的URL
  • 查找元素
    • getElementById("idName"):ie8及较低版本不区别ID的大小写,只返回文档中第一次出现的元素;ie7及较低版本name特性与给定ID匹配的表单元素(input、textarea、button、select)也会被该方法返回;为了避免这个问题,最好不让表单字段的name特性与其他元素的ID相同
    • getElementByTagName("tagName"):返回一个类数组的HTMLCollection对象,访问方法和NodeList一致;该Collection有namedItem("name")方法,可以按名称访问项(只能取得第一项),或者直接collection["name"]访问
    • getElementByName("name"):返回带有给定name特性的所有元素
  • 特殊集合
    • anchors:包含文档中所有带name特性的<a>元素
    • forms:包含文档中所有<form>元素
    • images:包含文档中所有<img>元素
    • links:包含文档中所有带href的<a>元素
  • 文档写入
    • write:接受一个字符串参数,原样写入
    • writeln:在字符串末尾添加一个换行符
      //注意写入脚本的格式
      document.write("<script type=\"text/javascript\" src=\"file.js\" >" + "<\/script>");
      
    • open和close分别用于打开和关闭网页的输出流,如果是在页面加载期间使用write()writeln()方法,则不需要用到
Element类型
  • 特征
    • nodeType = 1
    • nodeName = 元素标签名
    • nodeValue = null
    • parentNode = Document / Element
  • HTML
    • id:元素在文档中的唯一标识符
    • title:有关元素的附加说明信息
    • lang:元素内容的语言代码
    • dir:语言的方向
    • className:与元素class特性对应
  • 获取特性:getAttribute("name")
  • 设置特性:setAttribute("name", "value")
  • 删除特性:removeAttribute("name")
  • 遍历元素特性
    function outputAttributes(element) {
      var pairs = new Array(),
          attrName,
          attrValue,
          i,
          len;
      for (var i = 0; i < element.attributes.length; i++) {
        attrName = element.attributes[i].nodeName;
        attrValue = element.attributes[i].nodeValue;
        if(element.attributes[i].specified) {
          pairs.push({"name": attrName, "value": attrValue})  
        }
      }
    }
    
  • 创建元素:createElement("tagName")
Text类型
  • 特性
    • nodeType = 3
    • nodeName = “#text”
    • nodeValue = 节点所包含的文本
    • parentNode = 某个Element
  • 修改
    • appendData(text):将text添加到节点末尾
    • deleteData(offset, count):从offset位置开始删除count个字符
    • insertData(offset, text):在offset位置插入text
    • replaceData(offset, count, text):替换
    • splitText(offset):从offset指定的位置将当前文本节点分割成两个文本节点,返回offset后的文本
    • substringData(offset, count):提取offset指定位置开始到offset+count为止处的字符串
  • 创建文本节点:createTextNode("textNode/text")
DocumentFragment类型
  • 特性
    • nodeType = 11
    • nodeName = “#document-fragment”
    • nodeValue = null
    • parentNode = null
  • 创建文档片段
    var fragment = document.createDocumentFragment();
    
  • 假设我们要为一个列表添加列表项,如果逐个添加列表项,将会导致浏览器反复渲染(呈现),为避免这个问题,可以像下面这样使用一个文档片段来保存创建的列表项,然后再一次性将它们添加到文档中
    //创建一个虚拟占位节点
    var fragment = document.createDocumentFragment();
    var ul = document.getElementById("myList");
    var li = null;
    for(var i = 0; i < 3; i++) {
      li = document.createElement("li");
      li.appendChild(document.createTextNode("Item " + (i + 1)));
      fragment.appendChild(li);
    }
    ul.appendChild(fragment);
    

DOM操作技术

动态脚本
function loadScript(url) {
  var script = document.createElement("script");
  script.type = "text/javascript";
  script.src = url;
  document.body.appendChild(script);
}
loadScript("client.js");
  • 动态指的是当页面加载时不存在,页面加载完成后再动态添加到页面
动态样式
  • 外部文件
    function loadStyles(url) {
      var link = document.createElement("link");
      link.rel = "stylesheet";
      link.type = "text/css";
      link.href = url;
      var head = document.getElementByTagName("head")[0];
      head.appendChild(link);
    }
    
  • 文本
    function loadStylesString(css) {
      var style = document.createElement("style");
      style.type = "text/css";
      try {
        style.appendChild(document.createTextNode(css));
      } catch (e) {
        style.styleSheet.cssText = css;
      }
      var head = document.getElementByTagName("head")[0];
      head.appendChild(style);
    }
    
    操作表格
    var table = document.createElement("table");
    table.border = 1;
    table.width = "100%";
    var tbody = document.createElement("tbody");
    table.appendChild(tbody);
    //创建第一行
    tbody.insertRow(0);
    tbody.rows[0].insertCell(0);
    tbody.rows[0].cells[0].appendChild(documentElement.createTextNode("Cell 1,1"));
    tbody.rows[0].insertCell(1);
    tbody.rows[0].cells[1].appendChild(document.createTextNode("Cell 2,1"));
    document.body.appendChild(table);
    

重排和重绘

  • 理解:浏览器下载完页面中的所有组件——HTML标记、javascript、CSS、图片——之后会解析并生成两个内部数据结构
    • DOM树表示页面结构
    • 渲染树表示DOM节点如何显示
  • 重排发生场景:
    • 添加或者删除可见的DOM元素
    • 元素位置改变
    • 元素尺寸改变(外边距、内边距、边框厚度、宽度、高度)
    • 内容改变(文本改变、图片被尺寸不同的图片替代)
    • 页面渲染器初始化
    • 浏览器窗口尺寸改变
  • 渲染树的排队与刷新
    • 获取布局信息的操作会导致队列刷新
      • offsetTopoffsetLeftoffsetWidthoffsetHeight
      • scrollTopscrollLeftscrollWidthscrollHeight
      • clientTopclientLeftclientWidthclientHeight
      • getComputedStyle()(IE)
  • 使用批量修改DOM来减少重排和重绘
    • 步骤
      • 使元素脱离文档流
      • 对应用多重改变
      • 把元素带回文档中
    • 脱离文档的三种基本方法
      • 隐藏元素、应用修改、重新显示
      • 使用文档片段在当前DOM之外构建一个子树,再把它拷贝回文档
      • 将原始元素拷贝到一个脱离文档的节点中,修改副本,完成后再替换原始元素
  • 缓存布局信息
    • 减少布局信息的获取次数,获取后把它赋值给剧局部变量再操作局部变量

总结:DOM是语言中立的API,用于访问和操作HTML和XML文档。DOM1级将HTML和XML文档形象地看作一个层次化的节点树,可以使用JavaScript来操作这个节点数,进而改变底层文档的外观和结构。


文章作者: Susie Chang
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Susie Chang !
 上一篇
DOM详解(二) DOM详解(二)
选择符API querySelector: 接收一个CSS选择符,返回与该模式匹配的第一个元素,如果没有找到匹配的元素,则返回nullvar body = document.querySelector("body"); var myDiv
2018-10-12
下一篇 
事件详解 事件详解
事件流 事件流描述的是从页面中接收事件的顺序 IE的事件流叫做事件冒泡,即时间开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档) 事件捕获 的思想是不太具体的节点应该更早的接收到事件,而最具
2018-10-10
  目录