ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript 学習の概要 [9]、DOM Ready

JavaScript 学習の概要 [9]、DOM Ready

黄舟
黄舟オリジナル
2017-02-10 09:42:061172ブラウズ

1. DOM 要素、属性、テキストのツリー構造、つまりノードツリーがあります。 DOM を通じて、JS は動的 HTML を作成できます。これにより、Web ページに動的効果を表示したり、ユーザーとのインタラクティブな機能を実現したりできます。 DOM は、プログラムで HTML を動的に制御するためのインターフェース (API とも呼ばれます) を提供します。したがって、DOM は、HTML

に動的な相互作用と効果を与える JS の中核となります。 DOM を安全に操作したい場合は、ページ内のすべての HTML が DOM ノードに解析されるまで待ってから操作する必要があるため、DOMReady について理解する必要があります。 その前に、DOM ノードを確認しましょう。 (1)、共通ノードタイプ

共通ノードタイプは以下の7種類です:

ノードタイプ

手順91011

(2)ノードタイプの説明

要素ノード

    などのHTMLタグ要素です。

    属性ノードは、id、class、nameなどの要素ノードの属性です。属性ノードは要素ノードとみなされないため、属性は DOM 内のドキュメント ツリーの一部とみなされません。つまり、属性ノードはそれを含む要素ノードの一部であり、ドキュメント内の別個のノードではありません。がツリーに表示されます。

    テキストノードはテキストコンテンツのみを含むノードです。ドキュメント ツリー内の要素と属性のテキスト コンテンツは、テキスト ノードによって表され、さらに多くの情報を含めることも、空白だけを含めることもできます。

    コメントノードはドキュメント内のコメントであり、その形式はです。

    ドキュメントノードはドキュメント全体でドキュメントツリーのルートノード、はドキュメント内の他のすべてのノードの親ノードです。ここで注意すべき点は、ドキュメント ノードは HTML ドキュメントのルート要素ではないということです。DOM ツリーを構築する場合、ルート要素はルート ノードとして適切ではないため、ドキュメント ノードが存在し、ルート要素が HTML ドキュメントとして表示されます。ドキュメントノードの子ノード。 HTML ドキュメント コード全体をドキュメント ノードとして考えます。このノードには、ドキュメント タイプ ノード と要素ノード の 2 つの子ノードが含まれています。

    ドキュメントタイプノードすべてのドキュメントにはDocumentType属性があり、がドキュメントタイプノードです。

    ドキュメントフラグメントノード、は、ドキュメントの一部またはセクションを表す、ドキュメントツリーに属しません。 しかし、これは非常に便利な特別な動作を持っています。たとえば、DocumentFragment ノードがドキュメントに挿入されるように要求された場合、挿入されるのは DocumentFragment 自体ではなく、そのすべての子孫ノードです。このとき、DocumentFragment は、ドキュメントに順番に挿入されるノードを一時的に保存する便利なプレースホルダーになります。また、ドキュメントの切り取り、コピー、貼り付けなどの操作にも役立ちます。 JS コードに要素を挿入して定義される変数と同様、この変数は一時的なプレースホルダーとしてのみ機能します。これを文書フラグメントノードと呼びます。

    ドキュメントフラグメントを作成するには、親ノードの下にn個の子ノードを直接挿入できるdocument.createDocumentFragment()メソッドを使用できます。 理論的には、ドキュメントの断片化により DOM 操作のパフォーマンスが向上します。ドキュメントの断片化により、低バージョンのブラウザではページのパフォーマンスが大幅に向上しますが、高度なブラウザではパフォーマンスの向上はほとんどなく、パフォーマンスにも影響します。したがって、を使用することはお勧めできません。このことは基本的に排除されました。以下はドキュメントフラグメントの作成例です。

    例: ul要素の下にドキュメントフラグメントを挿入


    <body>
    <ul id="listNode"></ul>
    
    <script>
    //创建一个文档碎片
    var frag = document.createDocumentFragment();
    //使用循环设置创建10个li元素
    for(var i = 0; i < 10; i++){
        //创建一个li元素
        var oLi = document.createElement(&#39;li&#39;);
        //li元素显示的内容
        oLi.innerHTML = &#39;list &#39; + i;
        //创建完毕后插入文档碎片中
        frag.appendChild(oLi);
    }
    //最后将文档碎片插入到父节点ul元素下
    document.getElementById(&#39;listNode&#39;).appendChild(frag);
    </script>
    </body>

    (3)、ノードタイプ、ノード名、ノード値

      属性的一系列操作是与元素的类型息息相关的,如果我们不对元素的节点类型作判断,就不知道如何操作:例如:obj.xx = yy 还是 obj.setAttribute(xx, yy),setAttribute() 方法可添加一个新属性并指定值,或者把一个现有的属性设定为指定的值,如果我们知道节点的类型,就可以直接设置或者是使用方法设置,所以我们有必要判断节点的类型,避免耗费资源,造成意想不到的结果。判断节点类型可使用 nodeType 属性用数值常量进行判断,其操作很简单,就是判断某个节点的节点类型是否等于该节点类型的数值常量。

      实例:节点类型判断


    <body>
    <!-- 这是一个注释。 -->
    <div id="div1">这是一个div元素节点。</div>
    
    <script>
    //判断是否为注释节点
    var commentNode = document.body.childNodes[1];
    if(commentNode.nodeType == 8){
        alert('该节点是注释节点。');
    }
    
    //判断是否为元素节点
    var divNode = document.getElementById('div1');
    if(divNode.nodeType == 1){
        alert('该节点是元素节点。');
    }
    
    //判断是否为文本节点
    var textNode = document.getElementById('div1').childNodes[0];
    if(textNode.nodeType == 3){
        alert('该节点是文本节点。');
    }
    </script>

      其实我们在获取节点的子节点时,不使用 childNodes 属性,而使用 children 属性,就可以避免这一问题,因为通过 childNodes 属性返回的是子节点集合,不仅包括元素节点,而且还包括文本节点,浏览器会将标签之间的空白默认为文本节点,而使用 children 属性,则只返回元素节点,不包括文本节点,还不包括注释节点。

      节点名称和节点值,直接使用实例演示:


    <body>
    <!-- nodeName 和 nodeValue 演示 -->
    <div id="div1">节点名称和节点值。</div>
    <script>
    //元素节点 = nodeType:1
    var divNode = document.getElementById('div1');
    console.log('元素节点的节点名称和值:' + divNode.nodeName + '/' + divNode.nodeValue);
    //nodeName:返回 元素的标签名(DIV)全部大写。nodeValue:返回 null
    
    //属性节点 = nodeType:2
    var attrNode = divNode.attributes[0];
    console.log('属性节点的节点名称和值:' + attrNode.nodeName + '/' + attrNode.nodeValue);
    //nodeName:返回 属性的名称(id)。nodeValue:返回 属性的值(div1)
    
    //文本节点 = nodeType:3
    var textNode = divNode.childNodes[0];
    console.log('文本节点的节点名称和值:' + textNode.nodeName + '/' + textNode.nodeValue);
    //nodeName:返回 #text。nodeValue:返回 节点所包含的文本
    
    //(comment)注释节点    = nodeType:8
    var commentNode = document.body.childNodes[1];
    console.log('注释节点的节点名称和值:' + commentNode.nodeName + '/' + commentNode.nodeValue);
    //nodeName:返回 #comment。nodeValue:返回 注释的内容
    
    //(DocumentType)文档类型节点 = nodeType:10
    console.log('文档类型节点的节点名称和值:' + document.doctype.nodeName + '/' + document.doctype.nodeValue);
    //nodeName:返回 document的名称(html)。nodeValue:返回 null
    
    //(DocumentFragment)文档片段节点 = nodeType:11
    var farg = document.createDocumentFragment();
    console.log('文档片段节点的节点名称和值:' + farg.nodeName + '/' + farg.nodeValue);
    //nodeName:返回 #document-fragment。nodeValue:返回 null
    </script>

    2、DOMReady

      (1)、JS 在页面中的位置

      页面中的 JS 代码,可以引入外部的 JS 文件,也可以放在 标签中或 标签中,放在 body 中的 JS 会在页面加载的时候被执行,而 head 中的 JS 会在被调用的时候才执行。

      放在 标签中或 标签中 的区别:

      浏览器解析 HTML 文档是从上到下、从左到右依次进行的,如果把 JS 放在 head 里的话,则先被解析,但这时候 body 还没有被解析,所以会返回空值,也就是会出错。放在 head 中的 JS 代码会在页面加载完成之前就读取,而放在 body 中的 JS 代码,会在整个页面加载完成之后读取。这就说明了,如果我们想定义一个全局对象,而这个对象是页面中的某个按钮时,我们必须将其放入 body 中,道理很明显:如果放入head,那当你定义的时候,那个按钮都没有被加载,可能获得的是一个 undefind。

      脚本应该放置的位置:

      页面中的 JS 会在浏览器加载页面的时候被立即执行,有时候我们想让一段脚本在页面加载的时候执行,而有时候我们想在用户触发一个事件的时候执行脚本。

      需调用才执行的脚本或事件触发执行的脚本放在 HTML 的 head 部分中,head 部分中的脚本,可以保证脚本在任何调用之前被加载。

      当页面被加载时执行的脚本放在 HTML 的 body 部分。放在 body 部分的脚本通常被用来生成页面的内容。

      body 和 head 部分可同时有脚本:文件中可以在 body 和 head 部分同时存在脚本。

      外部脚本:

      有时候需要在几个页面中运行同样的脚本程序, 这时就需要用到外部脚本,而不需要在各个页面中重复的写这些代码。

      (2)、JS 在页面中的应用

      ①、放在 body 部分中:


    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>JavaScript实例</title>
    </head>
    <body>
    <h1 id="main">这是h1标题中的一些文本。</h1>
    <script>
    document.getElementById('main').style.color = 'red';
    </script>
    </body>
    </html>

      上面的实例,通过 JS 改变了 h1 标题的 color 属性,当打开页面时标题显示为红色。

      ②、放在 head 部分中:


    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>JavaScript实例</title>
    <script>
    document.getElementById('main').style.color = 'red';
    </script>
    </head>
    <body>
    <h1 id="main">这是h1标题中的一些文本。</h1>
    </body>
    </html>


      将同样的 JS 代码放在 head 部分中,就无法正常运行了,浏览器报错:未捕获的类型错误:不能读取一个空元素的样式属性。出现这样的错误就是没有分清 HTML标签和 DOM 节点之间的区别,HTML 是超文本标记语言,用于展示内容,而行为交互是需要通过 DOM 操作来实现的,HTML 标签要通过浏览器解析,才能变成 DOM 节点,当我们向地址栏输入一个 URL 时,开始加载页面,然后就能够看到内容,在这期间就有一个 DOM 节点构建的过程,节点是以树的形式组织的。JS 对于  DOM 的操作必须在 DOM 树构建完毕后,而上面的实例,head 部分中的 JS 会被浏览器先解析,但是这时候 body 中的元素还没有被解析,DOM树并没有构建完毕,所以就出事了。那如果就想把 JS 代码放在 head 部分中,还不想出错,该怎么解决呢?下文再做具体分析。

      (3)、DOMReady

      HTML 标签需要通过浏览器解析才会变成 DOM 节点,在刷新 URL 地址的时候就有 DOM 节点的构建过程,当页面上所有的 HTML 标签都转换为节点以后,DOM 树才构建完毕,这就简称为 DOMReady。

      那浏览器是如何将 HTML 标签解析变成节点的呢,浏览器是通过渲染引擎来实现的,渲染引擎的职责就是渲染,即把请求的 HTML 内容显示到浏览器屏幕上。所谓渲染,就是浏览器把请求到的 HTML 内容显示在屏幕上的过程,所谓渲染引擎,就是浏览器的内核,是浏览器最核心的东西。

      各大浏览器的渲染引擎:

      Firefox、Chrome 和 Safari 是基于两种渲染引擎构建的:

    Firefox は、Mozilla が独自に開発したレンダリング エンジンである Geoko カーネルを使用しており、Gecko は Windows、Linux、Mac OS などの主要なオペレーティング システムで実行できるクロスプラットフォーム カーネルでもあります。バツ。

    Safari と Chrome は両方とも WebKit カーネルを使用します。 WebKit と WebCore はどちらも KHTML の派生であり、KDE ​​によって開発された HTML ページ レンダリング エンジンの 1 つです。 KHTML には、Mozilla 製品で使用されている Gecko エンジンよりも高速で、構文エラーに対する耐性が低いという利点があります。 WebKit はオープン ソースのレンダリング エンジンで、元々は Linux プラットフォーム用に開発され、後に Apple によって Mac と Windows に移植されました。 そしてIEはインターネット

    Explorerで使用されるレンダリングエンジンであるTridentカーネル(MSHTMLとも呼ばれる)を使用しており、このカーネルプログラムは1997年のIE 4で初めて使用されました。それ以来、新しいテクノロジが継続的に追加され、IE の新しいバージョンがリリースされましたが、Trident は Windows プラットフォームでのみ使用できます。 rantingコンテンツを取得した後のレンダリングエンジンの基本的なプロセス:domツリーを構築するための&gt -&gt; ; レンダリング ツリーを描画します レンダリング エンジンは HTML の解析を開始し、タグを DOM ツリーに変換します。次に、これらのスタイル情報と HTML 内のスタイル タグを解析します。別のツリー、レンダー ツリー、DOM ツリーのレンダリングに使用されるツリー、レンダー ツリーを構築するために使用されます。

    r

    ender ツリーは、色やサイズなどの属性を含むいくつかの長方形で構成され、正しい順序で画面に表示されます。

    render ツリーが構築された後、レイアウトプロセスが実行され、画面上の各ノードの正確な座標が決定されます。次のステップは描画です。これは、レンダー ツリーを走査し、UI バックエンド レイヤーを使用して各ノードを描画します。

    ユーザーエクスペリエンスを向上させるために、レンダリングエンジンはできるだけ早くコンテンツを画面にレンダリングし、すべてのHTMLが完了するまで待機しないことに注意してください。解析が完了したら、レンダー ツリーを構築してレイアウトします。コンテンツの一部を解析して表示し、同時に残りのコンテンツをネットワーク経由でダウンロードしている可能性があります。

    ブラウザでページを開く一般的なプロセス:

    ① ブラウザのダウンロード順序は上から下であり、レンダリングの順序も上から下です。 . ダウンロードとレンダリングは同時に実行されます。

    ②. ページの特定の部分をレンダリングすると、その上のすべての部分がダウンロードされます(これは、関連するすべての要素がダウンロードされたことを意味するわけではありません)。

      ③、如果遇到语义解释性的标签嵌入文件(JS 脚本,CSS 样式),那么此时IE的下载过程会启用单独连接进行下载。

      ④、并且在下载后进行解析,解析过程中,停止页面所有往下元素的下载。

      ⑤、样式表在下载完成后,将和以前下载的所有样式表一起进行解析,解析完成后,将对此前所有元素(含以前已经渲染的)重新进行渲染。

      ⑥、JS、CSS中如有重复定义,则之后定义的函数将覆盖前面定义的函数。

     

    3、DOMReady 实现

      脚本在 HTML DOM 加载渲染布局显示完成后才能运行,且要放在 body 部分内容的后面,而 DOMReady 则定义使脚本无论放在哪里都能执行。

      (1)、使用定时器

      把 JS 代码放在 head 部分中,还不想出错,就是等 DOM 树构建完毕后,再执行脚本,那么是否可以使用定时器延迟脚本的执行,避免这个错误呢。


    <!DOCTYPE html><html><head>
        <meta charset="UTF-8">
        <title>JavaScript实例</title>
        <script>
        //在DomReady完毕后2秒执行。打开页面可以看到一个样式变化的过程。
        setTimeout(function (){
        document.getElementById('main').style.color = 'red';
    },2000);</script></head><body><h1 id="main">这是h1标题中的一些文本。</h1></body></html>


       测试上面的代码,可以看到,在打开页面时标题显示为黑色,过一会后才会转变为红色,虽然这样不会报错了,但这并不是我们想要的效果。那可以再将延迟时间缩短,不就解决了吗,我们将时间设置为 30 毫秒再试试。


    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>JavaScript实例</title>
    <script>
    //将事件缩至30毫秒
    setTimeout(function (){
        document.getElementById('main').style.color = 'red';
    },30);
    </script>
    </head>
    <body>
    <h1 id="main">这是h1标题中的一些文本。</h1>
    </body>
    </html>

      测试上面代码,在打开页面时显示为红色,但如果刷新页面的话,还是能看到黑色的闪动了一下,虽然无伤大雅,但这样还存在着一个很严重的问题,如果 DomReady 时间超过了 30 毫秒,那还是会出错,显然这方法是不可行的

      (2)、使用 window.onload

      window.onload 事件是在浏览器绘制完 DOM 节点,再加载完页面上的所有资源后,然后执行脚本。也就是说在文档解析渲染,资源加载完成之前,不让脚本执行。


    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>JavaScript实例</title>
    <script>
    window.onload = function (){
        document.getElementById('main').style.color = 'red';
    };
    </script>
    </head>
    <body>
    <h1 id="main">这是h1标题中的一些文本。</h1>
    </body>
    </html>

      测试上面的代码,打开页面后显示为红色,再刷新也不会出现黑色的闪动,显然该方法是可行的。如果把 JS 代码放在 head 部分中,一般情况下都需要绑定一个监听,即window.onload 事件,等全部的 HTML 文档渲染完成后,再执行代码,这样就妥妥的了。

      该方法在文档外部资源不多的情况下,是没什么问题,但如果网站有很多图片,我们要用 JS 做到在点击每张图片时弹出图片的 src 属性,这时候就有问题了,而且是出大事了,我们都知道 DOM 树很快就构建完成了,但是这么多图片还在缓慢的加载中,想要先执行 JS 的效果,就得等到所有的图片全部加载完毕后才能实现,而在这期间页面不会响应用户任何操作,浏览器就跟死了一般。所以使用 window.onload 对于很多实际的操作(比如DOM操作,事件绑定等)就显得太迟了,比如图片过多,window.onload 却迟迟不能触发,影响用户体验。而 DOMReady 就可以满足提前绑定事件的需求。

      (3)、jQuery 实现 DOMReady

      最简单的方法就是使用 jQuery,jQuery 是一个 JS 函数库,封装了大量的 JS 方法,使用 jQuery 可以极大地简化 JS 编程。


    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>JavaScript实例</title>
    <script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
    <script>
    //jQuery 实现
    $(document).ready(function (){
        document.getElementById('main').style.color = 'red';
    });
    </script>
    </head>
    <body>
    <h1 id="main">这是h1标题中的一些文本。</h1>
    </body>
    </html>

     

      (4)、JS 实现 DOMReady

      用 JS 实现 DOMReady 其实也很简单,可以添加一个监听事件,即 addEventListener,该方法的语法为:document.addEventListener("事件名称", 函数, false);,false 表示在冒泡阶段捕获。再传入DOMContentLoaded 事件,这个事件是从 onload 事件延伸而来的,当一个页面完成加载时,初始化脚本的方法是使用 onload 事件,该方法的缺点是仅在所有资源都完全加载后才被触发,如果页面的图片很多的话,从用户访问到 onload 触发可能需要较长的时间,所以开发人员随后创建了一种自定义事件,DOMReady,他在 DOM 构建之后、资源加载之前就可以被触发,他的表现形式就是 DOMContentLoaded 。jQuery 源码中也是使用该方法完成的。


    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>JavaScript实例</title>
    <script>
    function domReady(fn){
        document.addEventListener('DOMContentLoaded', fn, false);
    }
    
    domReady(function (){
        document.getElementById('main').style.color = 'red';
    });
    </script>
    </head>
    <body>
    <h1 id="main">这是h1标题中的一些文本。</h1>
    </body>
    </html>

     

      上面代码,我们给参数传入一个回调函数,将 DOMReady(封装函数) 方法封装为一个函数方便以后使用,该方法支持所有现代浏览器,但是不支持 IE9 之前的浏览器。

     

    4、HTML 嵌套规则

      了解 HTML 嵌套规则,是进行 DOM 操作的基础。

      HTML 存在许多种类型的标签,有的标签下面只允许特定的标签存在,这就叫 HTML 嵌套规则。

      如果不按 HTML 嵌套规则写,浏览器就不会正确解析,会将不符合嵌套规则的节点放到目标节点的下面,或者变成纯文本。

      所以在编写任何代码时,都需要按照规则编写,有利于解析,有利于操作,有利于优化,有利于维护,有利于重构。

      HTML 元素可简单的分为块状元素和内联元素两类。下面是一些需要注意的嵌套规则:

      ①、块元素可以包含内联元素或某些块元素,但内联元素却不能包含块元素,他只能包含其他的内联元素,li元素内可以包含 p 元素。


    <div>
        <h1></h1>
        <p></p>
    </div>
    
    <a href=""><span class=""></span></a>
    
    <ul>
        <li>
            <div></div>
        </li>
    </ul>


      ②、块元素不能包含在p元素内。

    <p>这是一些文本
        <div style="width:100px;height:200px;border:1px solid black;"></div>
    </p>
    
    <p>这是一些文本
        <ul>
            <li>苹果</li>
            <li>香蕉</li>
        </ul>
    </p>

      测试上面的代码,虽然他们都能被正常显示,但是并不处在同一层。

      ③、有几个特殊的块元素只能包含内联元素,不能包含块元素,这几个特殊的块元素是 h1-h6、p 和 dt。

      ④、块元素与块元素并列,内联元素与内联元素并列。

    <div>
        <h2></h2>
        <p></p>
    </div>
    
    <div>
        <a href=""></a>
        <span class=""></span>
    </div>

    5、DOM 操作

      DOM 节点是一个非常复杂的东西,对于他的每一个属性的访问,有可能会向上搜寻到 n 多个原型点,因此 DOM 操作是个很耗性能的操作。

      使用 DOM 操作很复杂,所以建议尽量使用现成的框架来实现业务,比如 MVVM 框架,将所有的 DOM 操作,都转交给框架内部做精细有效的处理。


    以上就是JavaScript学习总结【9】、DOM Ready的内容,更多相关内容请关注PHP中文网(www.php.cn)!

     

      

     

数値定数

Element(要素ノード) HTMLタグの要素。
1 Attr(属性ノード) 要素ノードの属性。
2 Text (テキストノード) 要素ノードまたは属性ノード内のテキストコンテンツ。
3 コメント(コメントノード) はコメントの内容を表します。
8 Document (ドキュメントノード) はドキュメント全体 (DOM ツリーのルートノード、つまり document ) を表します。
DocumentType (ドキュメントタイプノード) はドキュメントタイプノードです。
DocumentFragment(文書フラグメントノード) は、文書ツリーに属さない文書の一部または段落を表します。
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。