美文网首页
《DOM探索之基础详解篇》笔记

《DOM探索之基础详解篇》笔记

作者: 8eeb5fce5842 | 来源:发表于2016-03-03 20:34 被阅读418次

    慕课网 DOM探索之基础详解篇 学习笔记

    一、认识DOM

    • DOM 提供了用程序来动态控制HTML的接口。
    • DOM级别:
      • DOM 0级:实际上是不存在的,只是DOM历史坐标系的一个参照点而已,多指DHTML。
      • DOM 1级:DOM core,DOM HTML.
      • DOM 2级:DOM Views,DOM Events,DOM Style,DOM Traversal and Range.
      • DOM 3级:DOM Load And Save,DOM Validation.
    • Web浏览器对DOM的支持:
    3c49055b-1b1b-4a25-91d3-db3cdd20e2cc.png
    • 常用节点类型: nodeType
    c71689c8-5e78-4e79-8c9e-af32fcc2c210.png
    • nodeName 节点名称,nodeValue 节点值,attributes 获取属性
    8592f28b-e69c-47b6-8f43-5a961bf34685.png

    ```
    <body>

    <div id="container">这是一个元素节点</div>
    <script>
    var divNode = document.getElementById('container'); // 元素节点
    console.log(divNode.nodeName + '/' + divNode.nodeValue);
    // DIV/null

    var attrNode = divNode.attributes[0]; // 属性节点
    console.log(attrNode.nodeName + '/' + attrNode.nodeValue);
    // id/container
    
    var textNode = divNode.childNodes[0]; // 文本节点
    console.log(textNode.nodeName + '/' + textNode.nodeValue);
    // #text/这是一个元素节点
    
    var commentNode = document.body.childNodes[1]; // 注释节点
    console.log(commentNode.nodeName + '/' + commentNode.nodeValue);
    // #comment/nodeName,nodeValue 实验
    
    var docNode = document.doctype; // 文档节点
    console.log(docNode.nodeName + '/' + docNode.nodeValue);
    // html/null
    
    var fragNode = document.createDocumentFragment(); // 文档片段节点
    console.log(fragNode.nodeName + '/' + fragNode.nodeValue);
    // #document-fragment/null
    </script>
    </body>
    ```
    

    二、DOM Ready

    • 页面加载时,有一个DOM节点构建的过程,当页面上所有的HTML都转换成了节点以后,就叫做DOM Ready(DOM树构建完毕)。
      • 浏览器通过渲染引擎将HTML标签解析编程DOM节点。
        • 渲染引擎的职责是:把请求到的内容显示到浏览器屏幕上。默认情况下渲染引擎可显示HTML、XML文档及图片。通过插件,其可显示其他类型的文档,如PDF等。

    渲染引擎的渲染流程

    • 渲染:浏览器把请求到的HTML内容显示出来的过程。
    • 渲染引擎首先通过网络获得所请求文档的内容,通常以8k分块的方法来完成。
      • 之后的过程:(不包含浏览器加载外部资源的过程,如图片、脚本)
        1.解析HTML,构建DOM树(构建DOM节点)。
        2.构建渲染树(解析样式信息,包括外部的css文件、style标签中的样式)。渲染树由一些包含有各种属性的矩形组成,他们将会按照正确的顺序显 示到屏幕上。
        3.布局渲染树(布局DOM节点),执行布局的过程,将确定每个节点在屏幕上的确切坐标。
        4.绘制渲染树(绘制DOM节点,即遍历渲染树),使用UI后端层来绘制每个节点。
    9c5c9a80-c81a-4265-b784-f9d192a937a0.png
    • Webkit主要渲染流程:
    09de489c-0b25-4bde-af92-09746975f930.png
    - 拓展阅读浏览器内部工作原理

    DOM Ready 的实现策略

    54589b7c-179c-4f90-9b6e-a54a56ea920b.png
    • window.onload会完整加载页面后在触发。这些策略是DOM树加载完成就触发,无需等待其他东西的加载。
    • JQ就是这样的。
    • 自己来实现一个在标准和非标下模拟DOMContentLoaded的功能:
      function myReady(fn){
      //对于现代浏览器,对DOMContentLoaded事件的处理采用标准的事件绑定方式
      if ( document.addEventListener ) {
          document.addEventListener("DOMContentLoaded", fn, false);
      } else {
          IEContentLoaded(fn);
      }
      //IE模拟DOMContentLoaded
      function IEContentLoaded(fn) {
          var d = window.document;
          var done = false;
          //只执行一次用户的回调函数init()
          var init = function () {
              if (!done) {
                  done = true;
                  fn();
              }
          };
          (function () {
              try {
                  // DOM树未创建完之前调用doScroll会抛出错误
                  d.documentElement.doScroll('left');
              } catch (e) {
                  //延迟再试一次~
                  setTimeout(arguments.callee, 50);
                  return;
              }
              // 没有错误就表示DOM树创建完毕,然后立马执行用户回调
              init();
          })();
          //监听document的加载状态
          d.onreadystatechange = function() {
              // 如果用户是在domReady之后绑定的函数,就立马执行
              if (d.readyState == 'complete') {
                  d.onreadystatechange = null;
                  init();
              }
          }
      }
      

    }
    ```

    • 对比:
      var d = document;
        var msgBox = d.getElementById("showMsg");
        var imgs = d.getElementsByTagName("img");
        var time1 = null, time2 = null;
        myReady(function(){
            msgBox.innerHTML += "dom已加载!<br>";
            time1 = new Date().getTime();
            msgBox.innerHTML += "时间戳:" + time1 + "<br>";
        });
        window.onload = function(){
            msgBox.innerHTML += "onload已加载!<br>";
            time2 = new Date().getTime();
            msgBox.innerHTML += "时间戳:" + time2 + "<br>";
            msgBox.innerHTML +="domReady比onload快:" + (time2 - time1) + "ms<br>";
      

    三、判断元素的节点类型

    • 判定方法:
      • isElement:判断是否为元素节点。
      • isHTML:判断是否为HTML文档的元素节点。
      • isXML:判断是否为XML文档的元素节点。
      • contains:判断两个节点的包含关系。

    判断节点为元素节点 isElement

    • 简单版:
    var isElement = function (el){
                return !!el && el.nodeType === 1;
            }
    
    • 完美版:
    var isElement = function (obj) {
                if (obj && obj.nodeType === 1) {//先过滤最简单的
                    if( window.Node && (obj instanceof Node )){ //如果是IE9,则判定其是否Node的实例
                        return true; //由于obj可能是来自另一个文档对象,因此不能轻易返回false
                    }
                    try {//最后以这种效率非常差但肯定可行的方案进行判定
                        testDiv.appendChild(obj);  // 不是元素节点,不能执行该步
                        testDiv.removeChild(obj);
                    } catch (e) {
                        return false;
                    }
                    return true;
                }
                return false;
            }
    

    判断节点为HTML和XML元素节点 isHTML isXML

    • HTML是XML的子集。
    • isXML:
      // Sizzle, jQuery自带的选择器引擎
          var isXML = function(elem) {
              var documentElement = elem && (elem.ownerDocument || elem).documentElement;
              return documentElement ? documentElement.nodeName !== "HTML" : false;
          };
          console.log(isXML(document.getElementById("test"))); // false
      
          // 但这样不严谨,因为XML的根节点,也可能是HTML标签,比如这样创建一个XML文档
          try {
              var doc = document.implementation.createDocument(null, 'HTML', null);
              console.log(doc.documentElement);
              console.log(isXML(doc));
          } catch (e) {
              console.log("不支持creatDocument方法");
          }
      
      // mootools的slick选择器引擎的源码:
          var isXML = function(document) {
              return (!!document.xmlVersion) || (!!document.xml) || (toString.call(document) == '[object XMLDocument]')
                      || (document.nodeType == 9 && document.documentElement.nodeName != 'HTML');
          };
      
          // 精简版
          var isXML = window.HTMLDocument ? function(doc) {
              return !(doc instanceof HTMLDocument);
          } : function(doc) {
              return "selectNodes" in doc;
          }
      
      • 用功能判断来实现:(佳)
      var isXML = function(doc) {
              return doc.createElement("p").nodeName !== doc.createElement("P").nodeName;
          }
      
    • isHTML:
      var isHTML = function(doc) {
              return doc.createElement("p").nodeName === doc.createElement("P").nodeName;
          }
      
      • isHTMLElement:
      var isElement = function (obj) {
              if (obj && obj.nodeType === 1) {//先过滤最简单的
                  if( window.Node && (obj instanceof Node )){ //如果是IE9,则判定其是否Node的实例
                      return true; //由于obj可能是来自另一个文档对象,因此不能轻易返回false
                  }
                  try {//最后以这种效率非常差但肯定可行的方案进行判定
                      testDiv.appendChild(obj);
                      testDiv.removeChild(obj);
                  } catch (e) {
                      return false;
                  }
                  return true;
              }
              return false;
          }
          var isHTMLElement(el){
             if(isElement){
                return isXML(el.ownerDocument);
             }
             return false;
          }
      

    判断节点的包含关系 contains

    • 目前几乎所以浏览器都支持contains方法:
    <body>
        <div id="p-node">
            <div id="c-node">子节点内容</div>
        </div>
        <script>
            var pNode = document.getElementById("p-node");
            var cNode = document.getElementById("c-node").childNodes[0];
            alert(document.contains(cNode)); // Chrome:true IE:报错 因为IE的docuemnt下无contains方法
            alert(pNode.contains(cNode)); // Chrome:true IE:报错 因为IE下不能用contains判断文本节点
            // 只有判断元素节点时才全兼容,弹true
        </script>
    </body>
    
    • 自写全兼容版本方法:
      function fixContains(a, b) {
              try {
                  while ((b = b.parentNode)){ // 找到内层节点的父节点,找不到便停止
                      if (b === a){ // b现在是父节点,若等于a,则说明a为父节点
                          return true;
                      }
                  }
                  return false;
              } catch (e) {
                  return false;
              }
          }
      

    四、继承层次与嵌套规则

    DOM节点继承层次

    • 创建一个p元素,一共追溯了7层原型链,可见DOM是十分耗性能的 。
    b2fbb735-0e38-499f-b14f-e3ebd4a6d97e.png
    • 创建一个文本,要追溯6层:


      a82d56ad-ff35-454f-a0fc-923bb3ed43d0.png
    • 只是第一层的属性就有这么多,可见多用DOM会有多卡:
    2f545974-67f5-4563-8bfe-755b7c003c48.png

    HTML嵌套规则

    • HTML嵌套规则:HTML存在许多种类型的标签,有的标签下只允许特定的标签存在,这就叫HTML嵌套规则。
      • 不按HTML嵌套规则写,浏览器就不会正确解析,会将不符合嵌套规则的节点放到目标节点的下面,或者变为纯文本。
    • 块状元素和内联元素:
    ebb804c4-878f-459d-8f9f-a2171c5f8235.png 0d8a5912-d889-48ff-9ec9-6c4956581d51.png
    • 块状元素和内联元素嵌套规则:
    7b392fa5-1333-429f-af87-4a50be5be8d8.png 51064f6d-f23b-475b-9920-0681b3949049.png 69d68339-4832-455a-a3e1-5437cd2f659c.png c781fd87-d163-44d0-9535-7ff23fc35d4d.png 1a0cec79-cfb9-4d2b-bab8-28a4a2eb95cb.png
    • 某些特殊元素嵌套规则:
    04bc252f-b64c-4978-98fd-c2a4db902499.png 901f826b-01a7-46aa-94bc-ea83f6628e35.png 1e005ac5-1308-42a5-8702-7a271c3d7aa4.png 14b6c0bd-1d59-48c2-8242-5c9d3fc7f531.png 951be6f6-e5e9-42c5-b391-cd826ba1d13a.png

    相关文章

      网友评论

          本文标题:《DOM探索之基础详解篇》笔记

          本文链接:https://www.haomeiwen.com/subject/fwcokttx.html