Есть ли встроенная функция jQuery для переключения элементов?

174

Могу ли я легко поменять два элемента с помощью jQuery?

Я хочу сделать это с одной строкой, если это возможно.

У меня есть элемент select, и у меня есть две кнопки для перемещения вверх или вниз по опциям, и у меня уже есть селекторы selected и target, я делаю это с помощью if, но мне было интересно, есть ли более простой способ.

хуан
источник
Можете ли вы опубликовать разметку и пример кода?
Константин Таркус
Дело не в jQuery, а в JavaScript: вы не можете поменять элементы DOM одной инструкцией. Тем не менее, [ответ Паоло] (# 698386) является большой плагин;)
Себ
1
Для людей, которые приходят сюда из Google: посмотрите ответ lotif, очень простой и отлично сработавший для меня.
Морис
1
@Maurice, я изменил принятый ответ
JUAN
1
Это только меняет элементы, если они находятся рядом друг с другом! Пожалуйста, измените принятый ответ.
Сергей

Ответы:

233

Я нашел интересный способ решить эту проблему, используя только jQuery:

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

или

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

:)

lotif
источник
2
Да, это должно сработать: в документации сказано: «Если элемент, выбранный таким образом, вставлен в другое место, он будет перемещен до цели (не клонирован)».
Чудесно
12
Мне больше нравится этот вариант! Просто и приятно совместимо. Хотя мне нравится использовать el1.insertBefore (el2) и el1.insertAfter (el2) для удобства чтения.
Морис
6
Хотя есть один признак ... (который, я думаю, относится ко всем решениям на этой странице), так как мы манипулируем DOM здесь. Удаление и добавление не работает хорошо, если в перемещаемом элементе присутствует элемент iframe. 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);
}
bobince
источник
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
Ах, есть угловой случай, когда следующий родной брат а - это сам б. Исправлено в приведенном фрагменте. JQuery делал то же самое.
Бобинц
У меня есть очень чувствительный к порядку список, который должен сохранять события и идентификаторы, и это работает как шарм! Спасибо!
Текин
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);

Обратите внимание, что это работает, только если селекторы соответствуют только 1 элементу, в противном случае это может привести к странным результатам.

Паоло Бергантино
источник
2
нужно ли их клонировать?
хуан
Возможно, вы могли бы написать их напрямую, используя html ()?
Эд Джеймс
2
@Ed Вудкок Если вы это сделаете, вы потеряете связанные события на этих элементах, я уверен.
Алекс
2
Прекрасно работает, когда div не рядом друг с другом :)
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);
};
user2820356
источник
7
Это должен быть принятый ответ. Клонирование удаляет любые триггеры событий и не является необходимым. Я использую этот точный метод в моем коде.
thekingoftruth
1
Это лучший ответ! Я случайно получил ul в качестве дочернего элемента моих замененных элементов, который был сортируемым элементом jquery. После обмена с ответом Паоло Бергантино элементы списка больше не могли быть отброшены. С ответом user2820356 все по-прежнему работало нормально.
agoldev
21

Многие из этих ответов просто неверны для общего случая, другие неоправданно сложны, если они фактически даже работают. JQuery .beforeи .afterметоды делают большую часть того, что вы хотите сделать, но вам нужен третий элемент, как работают многие алгоритмы подкачки. Это довольно просто - создайте временный элемент 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) поставить временный div tempпередthis

2) шаг thisперед темthat

3) двигаться thatпослеtemp

3б) удалить temp

Тогда просто

$(selectorA).swapWith(selectorB);

ДЕМО: http://codepen.io/anon/pen/akYajE

robisrob
источник
2
Это лучший ответ, все остальные считают, что элементы находятся рядом друг с другом. Это работает в любой ситуации.
Брат
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');
Эрик Варнке
источник
Это лучшее решение, имхо, но не нужно, 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потом нужны только, если вы не можете гарантировать, что всегда есть элемент до (в моем случае это так)

halfbit
источник
Или вы можете просто проверить .prev()длину, а если 0, то .prepend()чтобы .parent():) Таким образом, вам не нужно создавать дополнительные элементы.
jave.web
2

взгляните на плагин jQuery "Swapable"

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

он построен на «Сортируемом» и выглядит как сортируемый (перетаскивание, заполнитель и т. д.), но заменяет только два элемента: перетаскиваемый и опускаемый Все остальные элементы не затрагиваются и остаются на своей текущей позиции.

vadimk
источник
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 );

});

... не стесняйтесь обернуть внутренний код в функцию :)

Пример fiddle имеет дополнительные события click, прикрепленные для демонстрации сохранения событий ...
Пример fiddle: 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>
jave.web
источник
2

Это мое решение для перемещения нескольких дочерних элементов вверх и вниз внутри родительского элемента. Хорошо работает для перемещения выбранных опций в списке ( <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"));
});
pistipanko
источник
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

Mistic
источник
1

Если у вас есть несколько копий каждого элемента, вам нужно сделать что-то в цикле, естественно. У меня была такая ситуация недавно. Два повторяющихся элемента, которые мне нужно было переключить, имели классы и контейнерный элемент div:

<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"));
});
AdamJones
источник
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();
zveljkovic
источник
1

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

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

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

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

делай наоборот.

Поэтому, если obj1 находится после obj3, а obj2 после obj4, и если вы хотите поменять местами obj1 и obj2, вы будете делать это следующим образом

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

Это должно сделать это, кстати, вы можете использовать .prev () и .next (), чтобы найти obj3 и obj4, если у вас уже нет какого-то индекса для него.

Чан Ву Данг Кхоа
источник
Это не просто подделка принятого ответа, в нем содержится плохая синтаксическая ошибка. Вы копировали эту пасту?
clockw0rk
да нет ?? Это рабочий код моей работы в моей предыдущей компании. Кроме того, я убедился, чтобы указать, как использовать и в какой ситуации. Почему мне нужно кого-то сорвать? Я также даю, как получить индекс объекта, поэтому принятый ответ был прокомментирован как незавершенный.
Чан Ву Данг Кхоа
тогда почему после $ () вместо after ($ ())
clockw0rk
о да, я не видел это, спасибо. Я ввожу его по памяти, не имею права сохранять этот код после того, как покину компанию
Тран Ву Данг Кхоа,
0

если nodeA и nodeB - братья и сестры, им нравится два <tr>в одном <tbody>, вы можете просто использовать $(trA).insertAfter($(trB))или $(trA).insertBefore($(trB))поменять их местами, это работает для меня. и вам не нужно звонить $(trA).remove()раньше, иначе вам нужно повторно привязать некоторые события клика на$(trA)

Spark.Bao
источник
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 ().

user84771
источник
1
это удаляет любые обратные вызовы и привязки
thekingoftruth
-2

Я думаю, что вы можете сделать это очень просто. Например, скажем, у вас есть следующая структура: ...

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

и результат должен быть

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

JQuery:

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

Я надеюсь, что это помогает!

Jernej Gololicic
источник
2
это решение уже было указано и не работает, если два элемента не находятся рядом друг с другом.
BernaMariano