Gibt es eine native jQuery-Funktion zum Wechseln von Elementen?


174

Kann ich problemlos zwei Elemente mit jQuery austauschen?

Ich möchte dies nach Möglichkeit mit einer Zeile tun.

Ich habe ein Auswahlelement und zwei Schaltflächen, um die Optionen nach oben oder unten zu verschieben. Ich habe bereits die Auswahl- und die Zielauswahl eingerichtet. Ich mache dies mit einem Wenn, aber ich habe mich gefragt, ob es einen einfacheren Weg gibt.


Können Sie Ihr Markup und Codebeispiel veröffentlichen?
Konstantin Tarkus

Es geht nicht um jQuery, sondern um JavaScript: Sie können DOM-Elemente nicht in einer einzigen Anweisung austauschen. Allerdings ist [Paolos Antwort] (# 698386) ein großartiges Plugin;)
Seb

1
Für Leute, die von Google hierher kommen: Schauen Sie sich die Antwort von lotif an, sehr einfach und perfekt für mich funktioniert.
Maurice

1
@Maurice, änderte ich die akzeptierte Antwort
juan

1
Dies tauscht die Elemente nur aus, wenn sie nebeneinander liegen! Bitte ändern Sie die akzeptierte Antwort.
Serhiy

Antworten:


233

Ich habe einen interessanten Weg gefunden, dies nur mit jQuery zu lösen:

$("#element1").before($("#element2"));

oder

$("#element1").after($("#element2"));

:) :)


2
Ja, das sollte funktionieren: In der Dokumentation heißt es: "Wenn ein auf diese Weise ausgewähltes Element an einer anderen Stelle eingefügt wird, wird es vor dem Ziel verschoben (nicht geklont)."
Ridcully

12
Ich mag diese Option am besten! Einfach und gut kompatibel. Obwohl ich el1.insertBefore (el2) und el1.insertAfter (el2) zur besseren Lesbarkeit verwenden möchte.
Maurice

6
Eine Nebenbemerkung (die meiner Meinung nach für alle Lösungen auf dieser Seite gilt), da wir hier das DOM manipulieren. Das Entfernen und Hinzufügen funktioniert nicht gut, wenn in dem Element, das Sie verschieben, ein Iframe vorhanden ist. Der Iframe wird neu geladen. Außerdem wird die ursprüngliche URL anstelle der aktuellen URL neu geladen. Sehr nervig aber sicherheitsrelevant. Ich habe keine Lösung dafür gefunden
Maurice

202
Dadurch werden zwei Elemente nur ausgetauscht, wenn die beiden Elemente unmittelbar nebeneinander liegen.
BonyT

12
Dies sollte nicht länger die akzeptierte Antwort sein. Im allgemeinen Fall funktioniert es nicht.
thekingoftruth

79

Paulo hat recht, aber ich bin mir nicht sicher, warum er die betreffenden Elemente klont. Dies ist nicht wirklich notwendig und verliert alle Referenzen oder Ereignis-Listener, die den Elementen und ihren Nachkommen zugeordnet sind.

Hier ist eine nicht klonende Version mit einfachen DOM-Methoden (da jQuery keine speziellen Funktionen hat, um diesen bestimmten Vorgang zu vereinfachen):

function swapNodes(a, b) {
    var aparent = a.parentNode;
    var asibling = a.nextSibling === b ? a : a.nextSibling;
    b.parentNode.insertBefore(a, b);
    aparent.insertBefore(b, asibling);
}

2
docs.jquery.com/Clone - Wenn Sie "true" übergeben, werden auch die Ereignisse geklont. Ich habe es versucht, ohne es zuerst zu klonen, aber es hat das getan, was deins derzeit ist: Es tauscht das erste gegen das zweite aus und lässt das erste so wie es ist.
Paolo Bergantino

Wenn ich <div id = "div1"> 1 </ div> <div id = "div2"> 2 </ div> habe und swapNodes (document.getElementById ('div1'), document.getElementById ('div2') aufrufe ); Ich bekomme <div id = "div1"> 1 </ div> <div id = "div2"> 1 </ div>
Paolo Bergantino

3
Ah, es gibt einen Eckfall, in dem das nächste Geschwister von a b selbst ist. Im obigen Snippet behoben. jQuery tat genau das Gleiche.
Bobince

Ich habe eine sehr auftragsabhängige Liste, in der Ereignisse und IDs gespeichert werden müssen, und das funktioniert wie ein Zauber! Vielen Dank!
Teekin

1
Ich denke, Sie sollten eine jQuery-Version hinzufügen, um den Titel dieser Frage technisch zu erfüllen. :)
thekingoftruth

70

Nein, gibt es nicht, aber Sie könnten eine zaubern:

jQuery.fn.swapWith = function(to) {
    return this.each(function() {
        var copy_to = $(to).clone(true);
        var copy_from = $(this).clone(true);
        $(to).replaceWith(copy_from);
        $(this).replaceWith(copy_to);
    });
};

Verwendung:

$(selector1).swapWith(selector2);

Beachten Sie, dass dies nur funktioniert, wenn die Selektoren jeweils nur einem Element entsprechen. Andernfalls kann es zu seltsamen Ergebnissen kommen.


2
ist es notwendig, sie zu klonen?
Juan

Vielleicht könnten Sie sie direkt mit html () schreiben?
Ed James

2
@ Ed Woodcock Wenn Sie dies tun, werden Sie gebundene Ereignisse für diese Elemente verlieren, da bin ich mir ziemlich sicher.
Alex

2
Funktioniert super, wenn die Divs nicht nebeneinander sind :)
Elmer

Dies sollte die akzeptierte Antwort sein. Es erweitert jQuery, um das bereitzustellen, wonach gefragt wurde.
Alice Wonder

40

Es gibt viele Randfälle zu diesem Problem, die nicht von der akzeptierten Antwort oder der Antwort von Bobince behandelt werden. Andere Lösungen, die das Klonen beinhalten, sind auf dem richtigen Weg, aber das Klonen ist teuer und unnötig. Wir sind versucht zu klonen, weil es ein uraltes Problem gibt, wie zwei Variablen ausgetauscht werden können. In einem der Schritte wird eine der Variablen einer temporären Variablen zugewiesen. Die Zuweisung (Klonen) ist in diesem Fall nicht erforderlich. Hier ist eine jQuery-basierte Lösung:

function swap(a, b) {
    a = $(a); b = $(b);
    var tmp = $('<span>').hide();
    a.before(tmp);
    b.before(a);
    tmp.replaceWith(b);
};

7
Dies sollte die akzeptierte Antwort sein. Das Klonen entfernt alle Ereignisauslöser und ist nicht erforderlich. Ich verwende genau diese Methode in meinem Code.
thekingoftruth

1
Das ist die bessere Antwort! Ich hatte als Kind eine Ul meiner ausgetauschten Elemente, was ein sehr sortierbares Element war. Nach dem Austausch mit Paolo Bergantinos Antwort konnten die Listenelemente nicht mehr gelöscht werden. Mit der Antwort von user2820356 funktionierte immer noch alles einwandfrei.
Agoldev

20

Viele dieser Antworten sind für den allgemeinen Fall einfach falsch, andere sind unnötig kompliziert, wenn sie überhaupt funktionieren. Die jQuery .beforeund die .afterMethoden machen das meiste, was Sie wollen, aber Sie benötigen ein drittes Element, wie viele Swap-Algorithmen funktionieren. Es ist ziemlich einfach: Erstellen Sie ein temporäres DOM-Element als Platzhalter, während Sie Dinge verschieben. Es besteht keine Notwendigkeit, Eltern oder Geschwister anzusehen, und sicherlich keine Notwendigkeit, zu klonen ...

$.fn.swapWith = function(that) {
  var $this = this;
  var $that = $(that);

  // create temporary placeholder
  var $temp = $("<div>");

  // 3-step swap
  $this.before($temp);
  $that.before($this);
  $temp.after($that).remove();

  return $this;
}

1) Stellen Sie das temporäre Div tempvorthis

2) bewegen , thisbevorthat

3) bewegen Sie sich thatnachtemp

3b) entfernen temp

Dann einfach

$(selectorA).swapWith(selectorB);

DEMO: http://codepen.io/anon/pen/akYajE


2
Dies ist die beste Antwort. Alle anderen gehen davon aus, dass die Elemente nebeneinander liegen. Dies funktioniert in jeder Situation.
Brohr

11

Sie sollten nicht zwei Klone benötigen, einer reicht aus. Wenn wir die Antwort von Paolo Bergantino nehmen, haben wir:

jQuery.fn.swapWith = function(to) {
    return this.each(function() {
        var copy_to = $(to).clone(true);
        $(to).replaceWith(this);
        $(this).replaceWith(copy_to);
    });
};

Sollte schneller sein. Das Übergeben des kleineren der beiden Elemente sollte ebenfalls die Dinge beschleunigen.


4
Das Problem dabei und bei Paolo ist, dass sie keine Elemente mit einer ID austauschen können, da IDs eindeutig sein müssen, damit das Klonen nicht funktioniert. Die Lösung von Bobince funktioniert in diesem Fall.
Ruud

Ein weiteres Problem besteht darin, dass dadurch die Ereignisse aus copy_to entfernt werden.
Jan Willem B

8

Ich habe vorher eine solche Technik angewendet. Ich verwende es für die Connector-Liste auf http://mybackupbox.com

// clone element1 and put the clone before element2
$('element1').clone().before('element2').end();

// replace the original element1 with element2
// leaving the element1 clone in it's place
$('element1').replaceWith('element2');

Dies ist die beste Lösung imo, aber keine Notwendigkeit, end()wenn Sie die Kette nicht fortsetzen. wie wäre es$('element1').clone().before('element2').end().replaceWith('element2');
Robisrob

1
Gerade bemerkt, clone()behält keine Abfrage bei, es data()sei denn, Sie geben an clone(true) - eine wichtige Unterscheidung, wenn Sie sortieren, damit Sie nicht alle Ihre Daten verlieren
Robisrob

3

Ich habe eine Funktion erstellt, mit der Sie mehrere ausgewählte Optionen nach oben oder unten verschieben können

$('#your_select_box').move_selected_options('down');
$('#your_select_boxt').move_selected_options('up');

Abhängigkeiten:

$.fn.reverse = [].reverse;
function swapWith() (Paolo Bergantino)

Zunächst wird geprüft, ob sich die erste / zuletzt ausgewählte Option nach oben / unten bewegen kann. Dann durchläuft es alle Elemente und Aufrufe

swapWith (element.next () oder element.prev ())

jQuery.fn.move_selected_options = function(up_or_down) {
  if(up_or_down == 'up'){
      var first_can_move_up = $("#" + this.attr('id') + ' option:selected:first').prev().size();
      if(first_can_move_up){
          $.each($("#" + this.attr('id') + ' option:selected'), function(index, option){
              $(option).swapWith($(option).prev());
          });
      }
  } else {
      var last_can_move_down = $("#" + this.attr('id') + ' option:selected:last').next().size();
      if(last_can_move_down){
        $.each($("#" + this.attr('id') + ' option:selected').reverse(), function(index, option){
            $(option).swapWith($(option).next());
        });
      }
  }
  return $(this);
}

Dies ist ineffizient, sowohl hinsichtlich der Art und Weise, wie der Code geschrieben wird, als auch hinsichtlich seiner Leistung.
Blaise

Es war kein Hauptmerkmal unserer App und ich benutze es nur mit kleinen Mengen an Optionen. Ich wollte nur etwas schnelles und einfaches ... Ich würde es lieben, wenn Sie dies verbessern könnten! Das wäre toll!
Tom Maeckelberghe

3

ein anderer ohne Klonen:

Ich muss ein tatsächliches und ein nominales Element austauschen:

            $nominal.before('<div />')
            $nb=$nominal.prev()
            $nominal.insertAfter($actual)
            $actual.insertAfter($nb)
            $nb.remove()

dann insert <div> beforeund removedanach werden nur benötigt, wenn Sie nicht sicherstellen können, dass es immer ein Element vor gibt (in meinem Fall ist es)


Oder Sie können einfach nach .prev()der Länge suchen und wenn 0, dann .prepend()nach .parent():) Auf diese Weise müssen Sie keine zusätzlichen Elemente erstellen.
jave.web

2

Schauen Sie sich das jQuery-Plugin "Swapable" an.

http://code.google.com/p/jquery-swapable/

Es basiert auf "Sortierbar" und sieht aus wie sortierbar (Drag-and-Drop, Platzhalter usw.), tauscht jedoch nur zwei Elemente aus: Ziehen und Ablegen. Alle anderen Elemente sind nicht betroffen und bleiben an ihrer aktuellen Position.


2

Dies ist eine Antwort, die auf der Antwortlogik von @lotif basiert, jedoch etwas allgemeiner ist

Wenn Sie append / prepend nach / vor die Elemente tatsächlich bewegt
=> keine clonning benötigt
=> Veranstaltungen gehalten

Es gibt zwei Fälle, die auftreten können

  1. Ein Ziel hat etwas " .prev()ious" => wir können das andere Ziel .after()so setzen.
  2. Ein Ziel ist das erste Kind davon .parent()=> wir können .prepend()das andere Ziel zum Elternteil machen.

Der Code

Dieser Code könnte noch kürzer gemacht werden, aber ich habe ihn aus Gründen der Lesbarkeit so gehalten. Beachten Sie, dass das Vorlagern von Eltern (falls erforderlich) und vorherigen Elementen obligatorisch ist.

$(function(){
  var $one = $("#one");
  var $two = $("#two");
  
  var $onePrev = $one.prev(); 
  if( $onePrev.length < 1 ) var $oneParent = $one.parent();

  var $twoPrev = $two.prev();
  if( $twoPrev.length < 1 ) var $twoParent = $two.parent();
  
  if( $onePrev.length > 0 ) $onePrev.after( $two );
    else $oneParent.prepend( $two );
    
  if( $twoPrev.length > 0 ) $twoPrev.after( $one );
    else $twoParent.prepend( $one );

});

... zögern Sie nicht, den inneren Code in eine Funktion zu verpacken :)

Beispiel-Geige hat zusätzliche Klickereignisse angehängt, um die Ereigniserhaltung zu demonstrieren ...
Beispiel-Geige: https://jsfiddle.net/ewroodqa/

... wird für verschiedene Fälle funktionieren - sogar für einen wie:

<div>
  <div id="one">ONE</div>
</div>
<div>Something in the middle</div>
<div>
  <div></div>
  <div id="two">TWO</div>
</div>

2

Dies ist meine Lösung, um mehrere untergeordnete Elemente innerhalb des übergeordneten Elements nach oben und unten zu verschieben. Funktioniert gut zum Verschieben ausgewählter Optionen in Listbox ( <select multiple></select>)

Nach oben gehen:

$(parent).find("childrenSelector").each((idx, child) => {
    $(child).insertBefore($(child).prev().not("childrenSelector"));
});

Sich abwärts bewegen:

$($(parent).find("childrenSelector").get().reverse()).each((idx, child) => {
    $(opt).insertAfter($(child).next().not("childrenSelector"));
});


1

Ich wollte eine Lösung, die clone () nicht verwendet, da sie Nebenwirkungen mit angehängten Ereignissen hat. Hier ist, was ich letztendlich getan habe

jQuery.fn.swapWith = function(target) {
    if (target.prev().is(this)) {
        target.insertBefore(this);
        return;
    }
    if (target.next().is(this)) {
        target.insertAfter(this);
        return
    }

    var this_to, this_to_obj,
        target_to, target_to_obj;

    if (target.prev().length == 0) {
        this_to = 'before';
        this_to_obj = target.next();
    }
    else {
        this_to = 'after';
        this_to_obj = target.prev();
    }
    if (jQuery(this).prev().length == 0) {
        target_to = 'before';
        target_to_obj = jQuery(this).next();
    }
    else {
        target_to = 'after';
        target_to_obj = jQuery(this).prev();
    }

    if (target_to == 'after') {
        target.insertAfter(target_to_obj);
    }
    else {
        target.insertBefore(target_to_obj);
    }
    if (this_to == 'after') {
        jQuery(this).insertAfter(this_to_obj);
    }
    else {
        jQuery(this).insertBefore(this_to_obj);
    }

    return this;
};

Es darf nicht mit jQuery-Objekten verwendet werden, die mehr als ein DOM-Element enthalten


1

Wenn Sie mehrere Kopien von jedem Element haben, müssen Sie natürlich etwas in einer Schleife tun. Ich hatte diese Situation vor kurzem. Die zwei sich wiederholenden Elemente, die ich wechseln musste, hatten Klassen und einen Container div als:

<div class="container">
  <span class="item1">xxx</span>
  <span class="item2">yyy</span>
</div> 
and repeat...

Mit dem folgenden Code konnte ich alles durchlaufen und umkehren ...

$( ".container " ).each(function() {
  $(this).children(".item2").after($(this).children(".item1"));
});

1

Ich habe es mit diesem Ausschnitt gemacht

// Create comments
var t1 = $('<!-- -->');
var t2 = $('<!-- -->');
// Position comments next to elements
$(ui.draggable).before(t1);
$(this).before(t2);
// Move elements
t1.after($(this));
t2.after($(ui.draggable));
// Remove comments
t1.remove();
t2.remove();

1

Ich habe eine Tabelle zum Ändern der Reihenfolge von obj in der verwendeten Datenbank .after () .before () erstellt, also ist dies von dem, was ich experimentiert habe.

$(obj1).after($(obj2))

Ist obj1 vor obj2 und einfügen

$(obj1).before($(obj2)) 

mach das umgekehrt.

Wenn also obj1 nach obj3 und obj2 nach obj4 steht und Sie die Orte obj1 und obj2 ändern möchten, werden Sie es wie folgt tun

$(obj1).before($(obj4))
$(obj2).before($(obj3))

Dies sollte es übrigens tun. Sie können .prev () und .next () verwenden, um obj3 und obj4 zu finden, wenn Sie noch keinen Index dafür hatten.


Dies ist nicht nur eine Abzocke der akzeptierten Antwort, sondern enthält einen schlechten syntaktischen Fehler. Hast du das kopypastiert?
clockw0rk

ehh nein ?? Dies ist der Arbeitscode für meine Arbeit in meiner vorherigen Firma. Außerdem habe ich darauf hingewiesen, wie und in welcher Situation zu verwenden. Warum muss ich jemanden abreißen? Ich gebe auch an, wie man einen Index des Objekts erhält, weshalb die akzeptierte Antwort als nicht vollständig kommentiert wurde.
Tran Vu Dang Khoa

Warum ist es dann nach $ () statt nach ($ ())
clockw0rk

Oh ja, ich habe es nicht gesehen, danke. Ich gebe es aus dem Speicher ein und habe nicht das Recht, diesen Code zu behalten, nachdem ich das Unternehmen verlassen habe
Tran Vu Dang Khoa

0

Wenn NodeA und NodeB Geschwister sind, zwei <tr>in derselben mögen <tbody>, können Sie sie einfach verwenden $(trA).insertAfter($(trB))oder austauschen $(trA).insertBefore($(trB)), es funktioniert für mich. und Sie müssen $(trA).remove()vorher nicht anrufen , sonst müssen Sie einige Klickereignisse erneut binden$(trA)


0
$('.five').swap('.two');

Erstellen Sie eine solche jQuery-Funktion

$.fn.swap = function (elem) 
{
    elem = elem.jquery ? elem : $(elem);
    return this.each(function () 
    {
        $('<span></span>').insertBefore(this).before(elem.before(this)).remove();
    });
};

Vielen Dank an Yannick Guinness unter https://jsfiddle.net/ARTsinn/TVjnr/



-2

Ich denke, Sie können es sehr einfach machen. Nehmen wir zum Beispiel an, Sie haben die nächste Struktur: ...

<div id="first">...</div>
<div id="second">...</div>

und das Ergebnis sollte sein

<div id="second">...</div>
<div id="first">...</div>

jquery:

$('#second').after($('#first'));

Ich hoffe, es hilft!


2
Diese Lösung wurde bereits erwähnt und funktioniert nicht, es sei denn, die beiden Elemente liegen unmittelbar nebeneinander.
BernaMariano
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.