Чи існує вроджена функція 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.