ホームページ  >  記事  >  ウェブフロントエンド  >  ノードメソッドを挿入する JavaScript 実装の概要

ノードメソッドを挿入する JavaScript 実装の概要

青灯夜游
青灯夜游転載
2018-10-15 15:10:455495ブラウズ

JavaScript にノードを挿入するにはどうすればよいですか?この記事では、JavaScript にノードを挿入するためのいくつかの方法をまとめます。困っている友人は参考にしていただければ幸いです。

JS ネイティブ API には、innerHTML、outerHTML、appendChild、insertBefore、insertAdjacentHTML、および applyElement という 6 つのノード挿入方法があります。

ここでは、それぞれの使用法を要約し、before、prepend、append、after、applyElement などの一連の関数をカプセル化します。

1. 6 つの使用方法

innerHTML: タグ内の HTML コンテンツを取得します。

outerHTML: ターゲットタグと内部 HTML を含むコンテンツを取得します。

appendChild: ターゲット ラベルの末尾に子ノードを追加し、パラメーター ノードを返します。

insertBefore: 最初のパラメーターを子ノードとしてターゲット ノードの 2 番目のパラメーターの位置に追加し、最初のパラメーターを返します。

insertAdjacentHTML: ターゲット ノードの指定された位置にノードを追加します。2 番目のパラメータは追加するノードで、最初のパラメータには beforebegin (previousSibling として追加)、afterbegin ( firstChild として追加)、beforeend (lastChild として追加)、afterend (nextSibling として追加)。また、insertAdjacentElement と insertAdjacentText という 2 つの兄弟関数もあります。前者は要素を追加して要素を返し、後者はテキストを追加します。

applyElement: IE の関数。パラメーター ノードをターゲット ノードの外側または内側の周囲に設定します。最初のパラメーターはパラメーター ノードで、2 番目のパラメーターは内部 (内側の周囲) を含むメソッドを指定します。パラメータ ノードはターゲット ノードの子ノードをラップします)、外側(ターゲット ノードを囲みます。つまり、パラメータ ノードがターゲット ノードをラップします)。

上記の 6 つのメソッドのうち最初の 2 つ (属性) を除き、他の 4 つは関数です。 innerHTML と externalHTML については何も言うことはありません。挿入の目的は、HTML 文字列を直接割り当てることで実現できます。insertBefore は、ノードの挿入を非常に柔軟に実装できます。文字列挿入メソッド; applyElement 関数は、互換性があれば JQuery の Wrap シリーズ関数になる可能性があります。

2. ターゲット ノードの前にパラメータ ノードを挿入する前に、before、prepend、append、after 関数を実装します。ターゲット ノードの親ノードを取得するだけで済みます。その後、親ノードは insertBefore を呼び出します。 prepend は、ターゲット ノードの最初の子ノードの位置にパラメーター ノードを挿入し、ターゲット ノードの最初の子ノードを取得して、insertBefore を呼び出します。

append の機能は、ネイティブ API の appendChild の機能と同じです。

afterターゲット ノードの後ろにパラメーター ノードを挿入し、ターゲット ノードの nextSibling を取得してから、insertBefore を呼び出します。

具体的な実装は次のとおりです:

var append = function(node, scope) {    
     if(node.nodeType === 1 || node.nodeType === 9 || node.nodeType === 11) {
        scope.appendChild(node);
    }
};
var prepend = function(node, scope) {    
     if(node.nodeType === 1 || node.nodeType === 9 || node.nodeType === 11) {
        scope.insertBefore(node, scope.firstChild);
    }
};
var before = function(node, scope) {    
     if(node.nodeType === 1 || node.nodeType === 9 || node.nodeType === 11) {
        scope.parentNode.insertBefore(node, scope);
    }
};
var after = function(node, scope) {    
     if(node.nodeType === 1 || node.nodeType === 9 || node.nodeType === 11) {
        scope.parentNode.insertBefore(node, scope.nextSibling);
    }
};

上記の関数は、要素ノード、ドキュメント ノード、およびドキュメント フラグメントのみを挿入できます。これらはすべてノード タイプです。文字列形式での挿入をサポートしたい場合は、insertAdjacentHTML をカプセル化する必要があります。

ここでは、ストラテジ パターンを使用して、実装したい 4 つの関数をストラテジ オブジェクトに作成し、それらを動的に生成してコードを合理化する効果を実現します。

//E5才有的迭代, 所以迭代要预先兼容
Array.prototype.forEach = [].forEach || function(callback) {    
      for(var i = 0, len = this.length; i < len; i++) {
        callback.call(this[i], this[i], i, this);
    }
};  
/**插入策略集合**/ 
var insertStrategies = {
    before : function(node, scope) {
        scope.parentNode.insertBefore(node, scope);
    },
    prepend : function(node, scope) {
        scope.insertBefore(node, scope.firstChild);
    },
    append : function(node, scope) {
        scope.appendChild(node);
    },
    after : function(node, scope) {
        scope.parentNode.insertBefore(node, scope.nextSibling);
    },    
    /*支持字符串格式的插入, 注意:要兼容不可直接做p子类的元素*/
    /*insertAdjace还有Element和Text,前者只能插元素,后者只能插文本*/
    beforestr : function(node, scope) {
        scope.insertAdjacentHTML(&#39;beforeBegin&#39;, node);
    },
    prependstr : function(node, scope) {
        scope.insertAdjacentHTML(&#39;afterBegin&#39;, node);
    },
    appendstr : function(node, scope) {
        scope.insertAdjacentHTML(&#39;beforeEnd&#39;, node);
    },
    afterstr : function(node, scope) {
        scope.insertAdjacentHTML(&#39;afterEnd&#39;, node);
    }
};
//响应函数
var returnMethod = function(method, node, scope) {    //如果是字符串
    if(typeof node === &#39;string&#39;) {        
        return insertStrategies[method + &#39;str&#39;](node, scope);
    }    
    //1(元素)、9(文档)、11(文档碎片)
    if(node.nodeType === 1 || node.nodeType === 9 || node.nodeType === 11) {        
          return insertStrategies[method](node, scope);
    }    
    //此处还可添加节点集合的处理逻辑,用于处理选择其引擎获取的节点集合。
};

[&#39;before&#39;, &#39;prepend&#39;, &#39;append&#39;, &#39;after&#39;].forEach(function(method){
    window[method] = function(node, scope) {
        returnMethod(method, node, scope);
    };
});

すべての関数が実装されていますwindow 属性として使用されるため、グローバル関数として直接呼び出すことができます。これらの関数はノード オブジェクトに属さないため、scope パラメーターはターゲット ノードを参照します。以下が applyElement と互換性を持った後、すべての関数を HTMLElement のプロトタイプに拡張し、要素ノードへの直接呼び出しの効果を実現するためにスコープ パラメーターを省略できるようにします。もちろん、フレームワークの場合、ネイティブ オブジェクトのプロトタイプの変更を避けるために、フレームワーク オブジェクトが通常生成され、その後、これらの関数がそのプロトタイプに拡張されます。

3. applyElement との互換性

applyElement 関数は IE のプライベート実装であるため、他のブラウザで使用したい場合は、 Array の forEach のようなものを使用する必要があります。また、オブジェクトのプロトタイプと互換性を持たせる必要があります。 実際、新しい要素にターゲット ノードまたはターゲット要素の子ノードを含める場合は、innerHTML と externalHTML を使用することもできます。しかし、必ず問題が発生します。新しい要素が DOM ツリーの一部であり、一連の子ノードもある場合、その子ノードはどのように処理されるべきでしょうか?所定の位置に留まるか、それともターゲット要素にマージされますか?ターゲット要素にマージされる場合、ターゲット要素が最初に来るのか、それとも新しい要素が最初に来るのか?

wrap シリーズ関数の効果は、新しい要素タグのコンテンツを削除することだけであり、その子要素の効果は、新しい要素の親ノードの子ノードになることです。このメソッドは、Range オブジェクトを使用して実装することもできます。もちろん、innerHTML および externalHTML を使用して実装することもできます。ここでは Range オブジェクトを使用して実装します。

/*兼容IE的applyElement*/
HTMLElement.prototype.removeNode = HTMLElement.prototype.removeNode || function(deep) {    
       if(this.parentNode) {        
            var range = this.ownerDocument.createRange();
        range.selectNodeContents(this);        
        if(!deep) {            
               var fragment = range.extractContents();
            range.setStartBefore(this);
            range.insertNode(fragment);
            range.detach();
        }        
        return this.parentNode.removeChild(this);
    }
}; 

HTMLElement.prototype.applyElement = HTMLElement.prototype.applyElement || function(node, where) {
    node = node.removeNode();
    where = (where || &#39;outside&#39;).toLowerCase();    
    var range = this.ownerDocument.createRange();    
    if(where === &#39;inside&#39;) {
        range.selectNodeContents(this);
        range.surroundContents(node);
        range.detach();
    }else if(where === &#39;outside&#39;) {
        range.selectNode(this);
        range.surroundContents(node);
        range.detach();
    }    
    return node;
};

4. すべての関数を HTMLElement のプロトタイプに拡張します。

この考え方は applyElement を修復するのと同じで、ウィンドウを変更するだけです。 [メソッド] HTMLElement.prototype[メソッド]に置き換え、バッチ生成時に渡すスコープをこれに変更します。

//E5才有的迭代, 所以迭代要预先兼容
Array.prototype.forEach = [].forEach || function(callback) {    
        for(var i = 0, len = this.length; i < len; i++) {
        callback.call(this[i], this[i], i, this);
    }
};  

/**插入策略集合**/
var insertStrategies = {
    before : function(node, scope) {
        scope.parentNode.insertBefore(node, scope);
    },
    prepend : function(node, scope) {
        scope.insertBefore(node, scope.firstChild);
    },
    append : function(node, scope) {
        scope.appendChild(node);
    },
    after : function(node, scope) {
        scope.parentNode.insertBefore(node, scope.nextSibling);
    },    
    /*支持字符串格式的插入, 注意:要兼容不可直接做p子类的元素*/
    /*insertAdjace还有Element和Text,前者只能插元素,后者只能插文本*/
    /**/
    beforestr : function(node, scope) {
        scope.insertAdjacentHTML(&#39;beforeBegin&#39;, node);
    },
    prependstr : function(node, scope) {
        scope.insertAdjacentHTML(&#39;afterBegin&#39;, node);
    },
    appendstr : function(node, scope) {
        scope.insertAdjacentHTML(&#39;beforeEnd&#39;, node);
    },
    afterstr : function(node, scope) {
        scope.insertAdjacentHTML(&#39;afterEnd&#39;, node);
    }
};
//响应函数
var returnMethod = function(method, node, scope) {    //如果是字符串
    if(typeof node === &#39;string&#39;) {   //低版本浏览器使用机会毕竟少数,每次都要判断很划不来。这段代码舍弃
        /*if(!scope.insertAdjacentHTML){
            throw new Error(&#39;(Firefox8-、IE4-、Opera7-、Safari4-)浏览器不能插入字符串!&#39;);
        }*/
        return insertStrategies[method + &#39;str&#39;](node, scope);
    }    //1(元素)、2(属性)、3(文本)、9(文档)、11(文档碎片)
    if(node.nodeType === 1 || node.nodeType === 9 || node.nodeType === 11) {        
            return insertStrategies[method](node, scope);
    }    
    //此处还可添加节点集合的处理逻辑(用文档碎片)
};

[&#39;before&#39;, &#39;prepend&#39;, &#39;append&#39;, &#39;after&#39;].forEach(function(method){
    HTMLElement.prototype[method] = function(node) {
        returnMethod(method, node, this);
    };
});/*兼容IE的applyElement*/
HTMLElement.prototype.removeNode = HTMLElement.prototype.removeNode || function(deep) {    
        if(this.parentNode) {        
                var range = this.ownerDocument.createRange();
        range.selectNodeContents(this);        
        if(!deep) {            
                var fragment = range.extractContents();
            range.setStartBefore(this);
            range.insertNode(fragment);
            range.detach();
        }        
        return this.parentNode.removeChild(this);
    }
}; 

HTMLElement.prototype.applyElement = HTMLElement.prototype.applyElement || function(node, where) {
    node = node.removeNode();
    where = (where || &#39;outside&#39;).toLowerCase();    
    var range = this.ownerDocument.createRange();    
    if(where === &#39;inside&#39;) {
        range.selectNodeContents(this);
        range.surroundContents(node);
        range.detach();
    }else if(where === &#39;outside&#39;) {
        range.selectNode(this);
        range.surroundContents(node);
        range.detach();
    }    return node;
};

5. テスト

テスト コードは次のとおりです:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>插入节点.html</title>
    <script type="text/javascript" src=&#39;./insertNode.js&#39;></script>
  </head>
  <body>
    <p id=&#39;p&#39; style=&#39;background:#888;border:1px solid #888;padding:10px;&#39;>坐标p</p>
    <p id=&#39;inside&#39; style=&#39;background:#0f0;border:1px solid #888;padding:10px;&#39;>inside</p>
    <p id=&#39;outside&#39; style=&#39;background:#F00;border:1px solid #888;padding:10px;&#39;>outside</p>
    
    <script type="text/javascript">        
            var p = document.getElementById('p');        
        var beforep = document.createElement('p');
        beforep.innerHTML = 'beforep';
        beforep.style.background = '#eee';        
        var prepEndp = document.createElement('p');
        prepEndp.innerHTML = 'prepEndp';
        prepEndp.style.background = '#ff0';        
        var appendp = document.createElement('p');
        appendp.innerHTML = 'appendp';
        appendp.style.background = '#0ff';        
        var afterp = document.createElement('p');
        afterp.innerHTML = 'afterp';
        afterp.style.background = '#f0f';
        
        p.before(beforep);
        p.prepend(prepEndp);
        p.append(appendp);
        p.after(afterp);        
        //测试文本插入
        p.append('[我是乐小天,我来测试直接插入文本]');        
        //测试外包含和内包含
        var outside = document.getElementById('outside');        
        var inside = document.getElementById('inside');
        outside.applyElement(inside, 'inside');    
     </script>
  </body>
</html>
結果の図:

参考文献: 「JavaScript フレームワーク設計」、「JavaScript 高度なプログラミング設計」、「JavaScript 設計パターンと開発実践」

要約: 上記はこの文書の要約です。記事 すべての内容が皆様の学習に役立つことを願っています。関連チュートリアルの詳細については、JavaScript ビデオ チュートリアル jQuery ビデオ チュートリアル bootstrap チュートリアル をご覧ください。

以上がノードメソッドを挿入する JavaScript 実装の概要の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcnblogs.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。