Чи існує вроджена функція jQuery для перемикання елементів?


174

Чи можу я легко поміняти два елементи jQuery?

Я хочу зробити це одним рядком, якщо можливо.

У мене є елемент вибору, і у мене є дві кнопки для переміщення вгору або вниз по параметрах, і у мене вже є вибрані та вибрані пункти призначення, я це роблю з if, але мені було цікаво, чи є простіший спосіб.


Чи можете ви опублікувати зразок розмітки та коду?
Костянтин Таркус

Це не питання jQuery, а JavaScript: ви не можете замінювати елементи DOM в одній інструкції. Тим НЕ менше, [відповідь Паоло] (# 698386) є великий плагін;)
Себ

1
Для людей, які приїжджають сюди з google: ознайомтеся з відповіддю lotif, дуже простою та чудово працюючою для мене.
Моріс

1
@Maurice, я змінив прийнятий відповідь
JUAN

1
Це обмінює елементи лише в тому випадку, якщо вони знаходяться один біля одного! Будь ласка, змініть прийняту відповідь.
Сергій

Відповіді:


233

Я знайшов цікавий спосіб вирішити це за допомогою лише jQuery:

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

або

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

:)


2
Так, це має спрацювати: У документації написано: "Якщо вибраний таким чином елемент буде вставлено в інше місце, він буде переміщений до цілі (не клонованої)".
Ridcully

12
Мені найбільше подобається цей варіант! Простий і приємно сумісний. Хоча я люблю використовувати для читабельності el1.insertBefore (el2) та el1.insertAfter (el2).
Моріс

6
Один осторонь, хоча .. (який, мабуть, стосується всіх рішень на цій сторінці), оскільки ми маніпулюємо DOM тут. Видалення та додавання не спрацьовує, якщо в елементі, який ви переміщуєте, присутній кадр. Iframe перезавантажиться. Також він перезавантажить його оригінальний URL замість поточного. Дуже дратує, але стосується безпеки. Я не знайшов рішення для цього
Моріс

202
це не замінює два елементи, якщо два елементи не знаходяться безпосередньо поруч.
BonyT

12
Це більше не повинно бути прийнятою відповіддю. Це не працює в узагальненому випадку.
thekingoftruth

79

Пауло має рацію, але я не впевнений, чому він клонує відповідні елементи. Це насправді не потрібно, і вони втратять будь-які посилання або слухачі подій, пов’язані з елементами та їх нащадками.

Ось нелоніфікована версія з використанням простих методів DOM (оскільки jQuery насправді не має спеціальних функцій для полегшення цієї конкретної операції):

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 - передача його "справжнього" також клонує події. Я спробував, не клонував її спочатку, але це робив те, що зараз є ваше: це поміняти перший на другий і залишити перший таким, як є.
Паоло Бергантіно

якщо у мене є <div id = "div1"> 1 </div> <div id = "div2"> 2 </div> і викликати swapNodes (document.getElementById ('div1'), document.getElementById ('div2') ); я отримую <div id = "div1"> 1 </div> <div id = "div2"> 1 </div>
Паоло Бергантіно

3
А, є кутовий випадок, коли наступним братом є сам b. Виправлено у наведеному фрагменті. jQuery робив саме те саме.
bobince

У мене дуже чутливий до порядку список, який повинен зберігати події та посвідчення особи, і це працює як шарм! Дякую!
Текін

1
Я вважаю, що вам слід додати версію jQuery, щоб технічно задовольнити назву цього питання. :)
thekingoftruth

70

Ні, немає, але ви можете згорнути один:

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

Використання:

$(selector1).swapWith(selector2);

Зверніть увагу, це працює лише в тому випадку, якщо селектори відповідають лише одному елементу, інакше це може дати дивні результати.


2
чи потрібно їх клонувати?
juan

Можливо, ви могли б записати їх безпосередньо за допомогою html ()?
Ед Джеймс

2
@Ed Woodcock Якщо ви це зробите, ви втратите пов'язані події на цих елементах, я впевнений.
alex

2
Чудово працює, коли діви не поруч один з одним :)
Elmer

Це має бути прийнятою відповіддю. Він розширює jQuery, щоб забезпечити те, про що вимагали.
Аліса Чудо

40

Існує дуже багато випадків цієї проблеми, які не розглядаються прийнятою відповіддю чи відповіддю бобінця. Інші рішення, що стосуються клонування, знаходяться на правильному шляху, але клонування є дорогим і непотрібним. Ми спокусилися клонуватися через вікову проблему заміни двох змінних, в якій одним із кроків є призначення однієї зі змінних тимчасовій змінній. Присвоєння (клонування) в цьому випадку не потрібне. Ось рішення, засноване на jQuery:

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

7
Це має бути прийнятою відповіддю. Клонування видаляє будь-які тригери подій і не є необхідним. Я використовую саме цей метод у своєму коді.
thekingoftruth

1
Це краща відповідь! Я сподіваюся, що у дитинстві є мій елемент, що мінявся своєю суттю, що було елементом сортування jquery. Після заміни на відповідь Паоло Бергантіно елементи списку більше не можна було скинути. З відповіддю користувача2820356 все ще працювало нормально.
agoldev

20

Багато з цих відповідей просто неправильні для загального випадку, інші - надмірно складні, якщо вони насправді навіть працюють. JQuery .beforeта .afterметоди роблять більшу частину того, що ви хочете зробити, але вам потрібен 3-й елемент, як працює багато алгоритмів своп. Це досить просто - зробіть тимчасовий елемент DOM як заповнювача під час переміщення речей. Не потрібно дивитися на батьків чи братів і сестер, і точно не потрібно клонувати ...

$.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) поставити тимчасовий дів tempранішеthis

2) рухатися thisранішеthat

3) рухатися thatпісляtemp

3б) видалити temp

Тоді просто

$(selectorA).swapWith(selectorB);

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


2
Це найкраща відповідь, всі інші припускають, що елементи знаходяться поруч. Це працює в будь-якій ситуації.
brohr

11

Вам не потрібно буде два клони, один зробить. Відповідаючи на Паоло Бергантіно, ми маємо:

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

Повинен бути швидшим. Проходження меншого з двох елементів також повинно прискорити роботу.


4
Проблема цього і Паоло полягає в тому, що вони не можуть замінювати елементи ідентифікатором, оскільки ідентифікатори повинні бути унікальними, щоб клонування не працювало. Рішення Бобінця в цьому випадку працює.
Рууд

Ще одна проблема полягає в тому, що це видалить події з copy_to.
Ян Віллем Б

8

Раніше я використовував таку техніку. Я використовую його для списку роз'ємів на 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');

це найкраще рішення imo, але це не потрібно, end()якщо ви не продовжуєте ланцюг. як щодо$('element1').clone().before('element2').end().replaceWith('element2');
robisrob

1
щойно помічено clone(), не зберігається жоден jquery, data()якщо ви не вкажете clone(true) - важлива відмінність при сортуванні, щоб ви не втратили всі свої дані
robisrob

3

Я створив функцію, яка дозволяє переміщувати кілька вибраних варіантів вгору або вниз

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

Залежності:

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

Спочатку він перевіряє, чи може перший / останній вибраний варіант рухатися вгору / вниз. Потім він проходить через всі елементи та дзвінки

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

Це неефективно, як у способі написання коду, так і в тому, як він буде виконуватись.
Блез

Це не було головною особливістю нашого додатку, і я просто використовую його з невеликою кількістю опцій. Я просто хотів чогось швидкого та легкого ... Мені б сподобалося, якби ти зміг це покращити! Було б чудово!
Том Меккельберге

3

інший без клонування:

У мене є фактичний та номінальний елемент, який потрібно міняти:

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

тоді insert <div> beforeі removeдалі потрібно лише, якщо ви не можете переконатися, що завжди є елемент для (у моєму випадку це)


Або ви можете просто перевірити .prev()довжину, а якщо 0, то .prepend()до .parent():). Таким чином, вам не потрібно створювати зайві елементи.
jave.web

2

погляньте на плагін jQuery "Swapable"

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

він побудований на "Sortable" і схожий на сортування (drag-n-drop, placeholder тощо), але міняється лише двома елементами: перетягування та скидання. На всі інші елементи не впливають і залишаються на своєму поточному положенні.


2

Це відповідь, заснована на логіці відповідей @lotif , але трохи більш узагальненою

Якщо додати / додати після / до того, як елементи будуть фактично переміщені
=> клонування не потрібно
=> події зберігаються

Є два випадки, які можуть статися

  1. Одна ціль має щось " .prev()ious" => ми можемо поставити іншу ціль, .after()що.
  2. Одна ціль є першою дитиною, це .parent()=> ми можемо .prepend()іншу націлити на батьків.

Код

Цей код можна зробити ще коротше, але я зберіг його таким чином для читання. Зауважте, що відновлення батьків (за потреби) та попередніх елементів є обов'язковим.

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

});

... сміливо вкладайте внутрішній код у функцію :)

Приклад скрипки має додаткові події клацання, що додаються для демонстрації збереження подій ...
Приклад скрипки: https://jsfiddle.net/ewroodqa/

... буде працювати в різних випадках - навіть таких, як:

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

2

Це моє рішення перемістити декілька дочірніх елементів вгору та вниз всередині батьківського елемента. Добре працює для переміщення вибраних параметрів у listbox ( <select multiple></select>)

Рухатися вгору:

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

Рухатися вниз:

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


1

Я хотів, щоб рішення відьом не використовувало clone (), оскільки воно має побічний ефект із приєднаними подіями, ось що я в кінцевому підсумку зробити

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

він не повинен використовуватися з об'єктами jQuery, що містять більше одного елемента DOM


1

Якщо у вас є кілька копій кожного елемента, вам потрібно зробити щось у циклі, природно. У мене така ситуація була нещодавно. У двох повторюваних елементах, які мені потрібно було переключити, були класи та роздільник контейнерів:

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

Наступний код дозволив мені повторити все і повернути назад ...

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

1

Я зробив це з цим фрагментом

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

Я робив таблицю для зміни порядку obj в базі даних, що використовується .after () .before (), так що це з чого я експериментую.

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

Вставити obj1 перед obj2 та

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

робити навпаки.

Отже, якщо obj1 після obj3, а obj2 після obj4, і якщо ви хочете змінити місця obj1 і obj2, ви зробите це як

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

Це слід зробити BTW, ви можете використовувати .prev () і .next (), щоб знайти obj3 та obj4, якщо у вас ще не був якийсь індекс для цього.


Це не просто перешкода прийнятого відповіді, вона містить погану синтаксичну помилку. Ви це копіювали?
clockw0rk

е, ні ?? Це робочий код для моєї роботи в попередній компанії. Поруч я обов'язково вказав, як користуватися та в якій ситуації. Чому мені потрібно зірвати когось? Я також даю, як отримати індекс об’єкта, через що прийняту відповідь прокоментували як незавершену.
Тран Ву Данг Хоа

то чому це після $ () замість після ($ ())
clockw0rk

о так, я не бачив цього, дякую. Я ввожу його з пам'яті, не маю права зберігати цей код після того, як я покину компанію
Tran Vu Dang Khoa

0

якщо nodeA і nodeB є побратимами, подобається два <tr>в одному <tbody>, ви можете просто використовувати $(trA).insertAfter($(trB))або $(trA).insertBefore($(trB))поміняти їх, це працює для мене. і вам не потрібно дзвонити $(trA).remove()раніше, інакше вам потрібно знову зв’язати деякі події клацання$(trA)


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

Створіть подібну функцію jQuery

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

Дякую Янніку Гіннесу на https://jsfiddle.net/ARTsinn/TVjnr/


-2

Найкращий варіант - клонувати їх методом clone ().


1
це видаляє всі зворотні дзвінки та прив’язки
thekingoftruth

-2

Я думаю, ви можете зробити це дуже просто. Наприклад, скажімо, у вас є наступна структура: ...

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

і результат повинен бути

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

jquery:

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

Я сподіваюся, що це допомагає!


2
це рішення вже вказувалося і не працює, якщо тільки два елементи знаходяться безпосередньо поруч.
BernaMariano
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.