Esiste una funzione nativa jQuery per cambiare gli elementi?


174

Posso scambiare facilmente due elementi con jQuery?

Sto cercando di farlo con una riga, se possibile.

Ho un elemento select e ho due pulsanti per spostare su o giù le opzioni, e ho già i selettori di selezione e destinazione in atto, lo faccio con un if, ma mi chiedevo se c'è un modo più semplice.


Puoi pubblicare un markup e un esempio di codice?
Konstantin Tarkus,

Non si tratta di jQuery ma di JavaScript: non è possibile scambiare elementi DOM in una singola istruzione. Tuttavia, [la risposta di Paolo] (# 698386) è un ottimo plugin;)
Seb

1
Per le persone che vengono qui da google: controlla la risposta di lotif, molto semplice e ha funzionato perfettamente per me.
Maurice,

1
@Maurice, ho cambiato la risposta accettata
juan

1
Questo scambia gli elementi solo se sono uno accanto all'altro! Per favore, cambia la risposta accettata.
Serhiy,

Risposte:


233

Ho trovato un modo interessante per risolverlo usando solo jQuery:

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

o

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

:)


2
Sì, dovrebbe funzionare: La documentazione dice: "Se un elemento selezionato in questo modo viene inserito altrove, verrà spostato prima della destinazione (non clonato)".
Ridcully,

12
Mi piace questa opzione la migliore! Semplice e ben compatibile. Anche se mi piace usare el1.insertBefore (el2) ed el1.insertAfter (el2) per leggibilità.
Maurice,

6
Un sidenote però ... (che suppongo si applichi a tutte le soluzioni in questa pagina) poiché stiamo manipolando il DOM qui. La rimozione e l'aggiunta non funzionano bene quando è presente un iframe all'interno dell'elemento che si sta spostando. L'iframe verrà ricaricato. Inoltre verrà ricaricato al suo url originale anziché a quello corrente. Molto fastidioso ma legato alla sicurezza. Non ho trovato una soluzione per questo
Maurice,

202
questo non scambia due elementi a meno che i due elementi non siano immediatamente uno accanto all'altro.
BonyT,

12
Questa non dovrebbe essere più la risposta accettata. Non funziona nel caso generalizzato.
thekingoftruth,

79

Paulo ha ragione, ma non sono sicuro del perché stia clonando gli elementi in questione. Questo non è davvero necessario e perderà qualsiasi riferimento o listener di eventi associato agli elementi e ai loro discendenti.

Ecco una versione non clonata che utilizza semplici metodi DOM (poiché jQuery non ha davvero alcuna funzione speciale per facilitare questa particolare operazione):

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 - passando "vero" si clonano anche gli eventi. Ho provato senza clonarlo prima, ma stava facendo quello che è attualmente il tuo: sta scambiando il primo con il 2 ° e lasciando il primo così com'è.
Paolo Bergantino,

se ho <div id = "div1"> 1 </div> <div id = "div2"> 2 </div> e chiamo swapNodes (document.getElementById ('div1'), document.getElementById ('div2') ); ottengo <div id = "div1"> 1 </div> <div id = "div2"> 1 </div>
Paolo Bergantino

3
Ah, c'è un caso in cui il prossimo fratello di a è b stesso. Risolto nel frammento sopra. jQuery stava facendo esattamente la stessa cosa.
mercoledì

Ho un elenco molto sensibile agli ordini che deve conservare eventi e ID e questo funziona come un incantesimo! Grazie!
Teekin,

1
Sento che dovresti aggiungere una versione jQuery per soddisfare tecnicamente il titolo di questa domanda. :)
thekingoftruth,

70

No, non c'è, ma potresti tirarne su uno:

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);
    });
};

Uso:

$(selector1).swapWith(selector2);

Nota che funziona solo se i selettori corrispondono solo a 1 elemento ciascuno, altrimenti potrebbe dare risultati strani.


2
è necessario clonarli?
juan,

Forse potresti scriverli direttamente usando html ()?
Ed James,

2
@Ed Woodcock Se lo fai, perderai eventi legati su quegli elementi di cui sono abbastanza sicuro.
alex,

2
Funziona alla grande quando i div non sono uno accanto all'altro :)
Elmer

Questa dovrebbe essere la risposta accettata. Estende jQuery per fornire ciò che è stato richiesto.
Alice Wonder,

40

Ci sono molti casi limite a questo problema, che non sono gestiti dalla risposta accettata o dalla risposta di Bobince. Altre soluzioni che prevedono la clonazione sono sulla buona strada, ma la clonazione è costosa e non necessaria. Siamo tentati di clonare, a causa del secolare problema di come scambiare due variabili, in cui uno dei passaggi è quello di assegnare una delle variabili a una variabile temporanea. L'assegnazione, (clonazione), in questo caso non è necessaria. Ecco una soluzione basata su jQuery:

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

7
Questa dovrebbe essere la risposta accettata. La clonazione rimuove eventuali trigger di eventi e non è necessaria. Uso questo metodo esatto nel mio codice.
thekingoftruth,

1
Questa è la risposta migliore! Sono felice di avere un ul come figlio dei miei elementi scambiati, che era un elemento ordinabile jquery. Dopo aver scambiato con la risposta di Paolo Bergantino le voci dell'elenco non potevano più essere eliminate. Con la risposta di user2820356 tutto funzionava ancora bene.
agoldev,

21

Molte di queste risposte sono semplicemente sbagliate per il caso generale, altre sono inutilmente complicate se addirittura funzionano. JQuery .beforee i .aftermetodi fanno la maggior parte di ciò che si desidera fare, ma è necessario un terzo elemento per il funzionamento di molti algoritmi di scambio. È piuttosto semplice: crea un elemento DOM temporaneo come segnaposto mentre sposti le cose. Non è necessario guardare genitori o fratelli, e certamente non è necessario clonare ...

$.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) metti tempprima il div temporaneothis

2) mossa thisprimathat

3) sposta thatdopotemp

3b) rimuovere temp

Quindi semplicemente

$(selectorA).swapWith(selectorB);

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


2
Questa è la risposta migliore, tutti gli altri suppongono che gli elementi siano uno accanto all'altro. Questo funziona in ogni situazione.
Brohr,

11

Non dovresti aver bisogno di due cloni, uno lo farà. Prendendo la risposta di Paolo Bergantino abbiamo:

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

Dovrebbe essere più veloce. Passare nel più piccolo dei due elementi dovrebbe anche accelerare le cose.


4
Il problema con questo, e quello di Paolo, è che non possono scambiare elementi con un ID poiché gli ID devono essere univoci, quindi la clonazione non funziona. La soluzione di Bobince funziona in questo caso.
Ruud,

Un altro problema è che questo rimuoverà gli eventi da copy_to.
Jan Willem B,

8

Ho usato una tecnica come questa prima. Lo uso per l'elenco dei connettori su 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');

questa è la migliore soluzione imo, ma non è necessario end()se non stai continuando la catena. che ne dici$('element1').clone().before('element2').end().replaceWith('element2');
robisrob il

1
appena notato clone()non conserva alcun jquery a data()meno che non specifichi clone(true) - una distinzione importante se stai ordinando in modo da non perdere tutti i tuoi dati
robisrob

3

Ho creato una funzione che ti consente di spostare più opzioni selezionate su o giù

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

dipendenze:

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

Innanzitutto controlla se la prima / ultima opzione selezionata è in grado di spostarsi su / giù. Quindi passa in rassegna tutti gli elementi e chiama

swapWith (element.next () o 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);
}

Questo è inefficiente, sia nel modo in cui il codice è scritto sia in come verrà eseguito.
Blaise,

Non era una caratteristica importante della nostra app e la utilizzo solo con piccole quantità di opzioni. Volevo solo qualcosa di semplice e veloce ... Mi piacerebbe se tu potessi migliorare questo! Sarebbe grandioso!
Tom Maeckelberghe,

3

un altro senza clonare:

Ho un elemento reale e un elemento nominale da scambiare:

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

allora insert <div> beforee removepoi sono necessari solo, se non puoi assicurarti, che ci sia sempre un elemento prima (nel mio caso lo è)


Oppure si può solo verificare la presenza di .prev()'s di lunghezza, e se 0, poi .prepend()a .parent():) In questo modo non è necessario creare elementi extra.
jave.web,

2

dai un'occhiata al plug-in jQuery "Scambiabile"

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

è basato su "Ordinabile" e sembra ordinabile (drag-n-drop, segnaposto, ecc.) ma scambia solo due elementi: trascinato e rilasciato. Tutti gli altri elementi non sono interessati e rimangono nella loro posizione corrente.


2

Questa è una risposta basata su @lotif logica di risposta di , ma un po 'più generalizzata

Se aggiungi / anteponi dopo / prima che gli elementi vengano effettivamente spostati
=> nessuna clonazione necessaria
=> eventi mantenuti

Ci possono essere due casi

  1. Un obiettivo ha qualcosa di " .prev()ious" => possiamo mettere l'altro obiettivo .after()che.
  2. Un obiettivo è il primo figlio di esso .parent()=> possiamo .prepend()l'altro obiettivo al genitore.

Il codice

Questo codice potrebbe essere fatto anche più breve, ma l'ho tenuto in questo modo per leggibilità. Nota che i genitori di prestigio (se necessario) e gli elementi precedenti sono obbligatori.

$(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 );

});

... sentiti libero di racchiudere il codice interno in una funzione :)

Esempio di violino con eventi di clic aggiuntivi associati per dimostrare la conservazione degli eventi ...
Esempio di violino: https://jsfiddle.net/ewroodqa/

... funzionerà per vari casi, anche uno come:

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

2

Questa è la mia soluzione per spostare più elementi figlio su e giù all'interno dell'elemento genitore. Funziona bene per spostare le opzioni selezionate nella casella di riepilogo (<select multiple></select> )

Andare avanti:

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

Abbassati:

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


1

Volevo che una strega non usasse clone () in quanto ha un effetto collaterale con eventi collegati, ecco cosa ho finito per fare

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;
};

non deve essere utilizzato con oggetti jQuery contenenti più di un elemento DOM


1

Se hai più copie di ogni elemento devi fare qualcosa in un ciclo naturalmente. Ho avuto questa situazione di recente. I due elementi ripetitivi che avevo bisogno di cambiare avevano le classi e un div contenitore così:

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

Il seguente codice mi ha permesso di scorrere tutto e invertire ...

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

1

L'ho fatto con questo frammento

// 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

Ho fatto una tabella per cambiare l'ordine di obj nel database usato .after () .before (), quindi questo è da quello che ho esperimento.

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

È inserire obj1 prima di obj2 e

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

fare il viceversa.

Quindi se obj1 è dopo obj3 e obj2 dopo di obj4, e se vuoi cambiare posto obj1 e obj2 lo farai come

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

Questo dovrebbe farlo BTW, puoi usare .prev () e .next () per trovare obj3 e obj4 se non avevi già qualche tipo di indice per esso.


Non è solo una fregatura della risposta accettata, contiene un cattivo errore sintattico. Hai copypasta questo?
clockw0rk,

eh no ?? Questo è un codice funzionante per il mio lavoro presso la mia precedente azienda. Inoltre mi sono assicurato di indicare come usare e in quale situazione. Perché ho bisogno di fregare qualcuno? Dò anche come ottenere l'indice dell'oggetto, motivo per cui la risposta accettata è stata commentata come non completata.
Tran Vu Dang Khoa,

allora perché è dopo $ () anziché after ($ ())
clockw0rk

oh sì, non l'ho visto, grazie. Lo inserisco dalla memoria, non ho il diritto di conservare quel codice dopo che ho lasciato l'azienda
Tran Vu Dang Khoa,

0

se nodeA e nodeB sono fratelli, ne piacciono due <tr>allo stesso modo <tbody>, puoi semplicemente usarli $(trA).insertAfter($(trB))o $(trA).insertBefore($(trB))scambiarli, funziona per me. e non è necessario chiamare $(trA).remove()prima, altrimenti è necessario ricollegare alcuni eventi di clic$(trA)


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

Crea una funzione jQuery come questa

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

Grazie a Yannick Guinness a https://jsfiddle.net/ARTsinn/TVjnr/



-2

Penso che tu possa farlo in modo molto semplice. Ad esempio, supponiamo che tu abbia la prossima struttura: ...

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

e il risultato dovrebbe essere

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

jQuery:

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

Spero possa essere d'aiuto!


2
questa soluzione è stata già indicata e non funziona, a meno che i due elementi non siano immediatamente uno accanto all'altro.
Berna Mariano,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.