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.

Juan
quelle
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"));

:) :)

Lotif
quelle
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);
}
Bobince
quelle
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.

Paolo Bergantino
quelle
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);
};
user2820356
quelle
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

Robisrob
quelle
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.

Matthew Wilcoxson
quelle
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');
Eric Warnke
quelle
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);
}
Tom Maeckelberghe
quelle
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)

Halbbit
quelle
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.

vadimk
quelle
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>
jave.web
quelle
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"));
});
Pistipanko
quelle
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

Mistic
quelle
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"));
});
AdamJones
quelle
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();
zveljkovic
quelle
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.

Tran Vu Dang Khoa
quelle
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)

Spark.Bao
quelle
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/

Luftig
quelle
-2

Die beste Option ist, sie mit der Methode clone () zu klonen.

user84771
quelle
1
Dies entfernt alle Rückrufe und Bindungen
thekingoftruth
-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!

Jernej Gololicic
quelle
2
Diese Lösung wurde bereits erwähnt und funktioniert nicht, es sei denn, die beiden Elemente liegen unmittelbar nebeneinander.
BernaMariano