web-dev-qa-db-ja.com

2つのhtml要素を交換し、それらのイベントリスナーを保持します

同様の質問がありますが、すべての答えは、内部のコンテンツに対してのみhtml要素を交換することです。

たくさんのコンテンツ(テーブル、選択ボックス、入力など)を含む2つのdivを交換する必要があります。要素にはイベントリスナーがあるので、メインのdivを交換した後にそれらを保持する必要があります。

JQuery1.5にアクセスできます。だから答えはOKです。

26
Ext

イベントハンドラーを失ったり、DOM参照を壊したりせずに、2つのdivを交換するには、それらをDOM内で移動するだけです。重要なのは、innerHTMLを変更しないことです。これは、新しいDOMノードを最初から再作成し、それらのDOMオブジェクト上の以前のイベントハンドラーがすべて失われるためです。

ただし、DOM要素をDOM内の新しい場所に移動するだけでは、DOM要素自体を変更せずにのみ親が変更されるため、すべてのイベントがアタッチされたままになります。

これは、DOM内の2つの要素を交換するクイック関数です。一方が他方の子でない限り、任意の2つの要素で機能するはずです。

function swapElements(obj1, obj2) {
    // create marker element and insert it where obj1 is
    var temp = document.createElement("div");
    obj1.parentNode.insertBefore(temp, obj1);

    // move obj1 to right before obj2
    obj2.parentNode.insertBefore(obj1, obj2);

    // move obj2 to right before where obj1 used to be
    temp.parentNode.insertBefore(obj2, temp);

    // remove temporary marker node
    temp.parentNode.removeChild(temp);
}

ここで動作することがわかります: http://jsfiddle.net/jfriend00/NThjN/


そして、これは一時的な要素が挿入されていなくても機能するバージョンです:

function swapElements(obj1, obj2) {
    // save the location of obj2
    var parent2 = obj2.parentNode;
    var next2 = obj2.nextSibling;
    // special case for obj1 is the next sibling of obj2
    if (next2 === obj1) {
        // just put obj1 before obj2
        parent2.insertBefore(obj1, obj2);
    } else {
        // insert obj2 right before obj1
        obj1.parentNode.insertBefore(obj2, obj1);

        // now insert obj1 where obj2 was
        if (next2) {
            // if there was an element after obj2, then insert obj1 right before that
            parent2.insertBefore(obj1, next2);
        } else {
            // otherwise, just append as last child
            parent2.appendChild(obj1);
        }
    }
}

作業デモ: http://jsfiddle.net/jfriend00/oq92jqrb/

52
jfriend00

これは、実際に要素をリロードせずに2つの要素を交換するためのより単純な関数です...

function swapElements(obj1, obj2) {
    obj2.nextSibling === obj1
    ? obj1.parentNode.insertBefore(obj2, obj1.nextSibling)
    : obj1.parentNode.insertBefore(obj2, obj1); 
}

注:obj1にYouTubeのような埋め込みビデオがある場合、交換してもリロードされません。変更されたのは要素の位置だけです。

9
Robert

2つの要素のparentNodesと1つのnextSiblingを追跡している場合は、一時的なプレースホルダーなしで2つの要素を子と交換できます。

2番目の要素が一人っ子、またはその親の最後の子である場合、その置換は(適切に)親に追加されます。

function swap(a, b){
    var p1= a.parentNode, p2= b.parentNode, sib= b.nextSibling;
    if(sib=== a) sib= sib.nextSibling;
    p1.replaceChild(b, a);
    if(sib) p2.insertBefore(a, sib);
    else p2.appendChild(a);
    return true;
}
1
kennebec

私の単純な関数は、最短で最も広く互換性があるようです。プレースホルダーやリロード要素など、面倒なものは必要ありません。

function swapElements(el1, el2) {
    var p2 = el2.parentNode, n2 = el2.nextSibling
    if (n2 === el1) return p2.insertBefore(el1, el2)
    el1.parentNode.insertBefore(el2, el1);
    p2.insertBefore(el1, n2);
}
0
cronoklee

O1.swapNode(o2);という事実を考えると、これはすべて非常に奇妙です。 IEで非常に長い間非常にうまく機能します。オブジェクトノードとテキストノードが混在している私の場合、1つのバージョンのみで機能します(すでにここに示されています):

let t = document.createElement("div");
o1.parentNode.insertBefore(t, o1);
o2.parentNode.insertBefore(o1, o2);
t.parentNode.insertBefore(o2, t);
t.parentNode.removeChild(t);

はい、一時ノードを作成するのは嫌いですが、このトリックのない上記のバージョンは、(関数を呼び出さずに)直接使用すると失敗します。関数を呼び出したくない場合があります。

これは、イベントがスワップしている要素にどのようにバインドされているかによって多少異なります。 jQueryを使用している場合、本当に必要なのは this の答えだけです。

主なことは、要素をdeleteまたはremoveChildnodeするのではなく、htmlをコピーして別の場所に貼り付けることです。そうすることで、一部のブラウザーはすべてのリソース(イベントハンドラーを含む)をクリアするため、バインドされたイベントは失われます。 IEは、動的にバインドされたイベントに関してメモリリークが発生することで有名ですが、実際には、さまざまなブラウザがどのように動作するかを予測するのは困難です。

基本的には、HTML文字列をコピーして貼り付ける以外は、要素を好きなように交換し、jQueryの.live()メソッドを使用してイベントをバインドします。 1.5には.onメソッドがあるとは思わないので

0

要素を並べて交換する場合は、非常に簡単です。

_<div class="container">
   <div class="item">Item 1</div>
   <div class="item">Item 2</div>
</div>
_

$('.container .item:nth-child(2)').insertBefore('.container .item:nth-child(1)');

0
Andrew Gale

JQueryは質問でタグ付けされているので、ここにjQueryソリューションがあります。

  $('#el_to_move').appendTo('#target_parent_el');

それでおしまい。 jQueryはそれを切り取って新しい場所に貼り付けます。


これも役立つ可能性があります。

https://api.jquery.com/detach/

0
cssyphus