Использование jQuery для замены одного тега другим

101

Цель:

Используя jQuery, я пытаюсь заменить все вхождения:

<code> ... </code>

с участием:

<pre> ... </pre>

Мое решение:

Я дошел до следующего,

$('code').replaceWith( "<pre>" + $('code').html() + "</pre>" );

Проблема с моим решением:

но проблема в том, что он заменяет все между тегами «code» (вторым, третьим, четвертым и т. д.) содержимым между первыми тегами «code».

например

<code> A </code>
<code> B </code>
<code> C </code>

становится

<pre> A </pre>
<pre> A </pre>
<pre> A </pre>

Я думаю, мне нужно использовать «это» и какую-то функцию, но боюсь, что я все еще учусь и не совсем понимаю, как собрать решение воедино.

Джон
источник
Исправлено решение оборачивать-разворачивать сейчас, Джон - проверьте это;)
Йенс Роланд

Ответы:

157

Вы можете передать функцию в .replaceWith [docs] :

$('code').replaceWith(function(){
    return $("<pre />", {html: $(this).html()});
});

Внутри функции thisотносится к обрабатываемому в данный момент codeэлементу.

ДЕМО

Обновление: Там нет никакой большой разницы в производительности , но в случае codeэлементы имеют другие HTML детей, добавление детей вместо сериализации их чувствует себя более правильным:

$('code').replaceWith(function(){
    return $("<pre />").append($(this).contents());
});
Феликс Клинг
источник
1
это кажется наиболее элегантным решением, хотя я признаю, что не понимаю, что происходит внутри функции replaceWith. хотелось бы услышать больше. т.е. как присваиваются открывающие и закрывающие теги? делает ли это пространство в <pre />?
jon
2
@jon: Это сокращение для создания новых элементов. Более подробную информацию можно найти здесь: api.jquery.com/jQuery/#jQuery2 . jQuery перебирает все элементы и выполняет функцию для каждого из них (то же самое, что .eachделает).
Феликс Клинг
@jon: Но проверьте решение Йенса еще раз, он его исправил.
Феликс Клинг
1
+1 за приятный трюк - его можно использовать не только для простой замены тега на тег.
Йенс Роланд
@FelixKling Разве это не приведет к перезаписи атрибутов тегов кода?
tewathia
80

Это намного приятнее:

$('code').contents().unwrap().wrap('<pre/>');

Хотя надо признать, что решение Феликса Клинга примерно в два раза быстрее :

Йенс Роланд
источник
4
работал как шарм, на мой взгляд, это наиболее эффективное решение. :)
jon
Ага. да, вы были правы. у меня тоже не сработало. спасибо, что поймали это!
jon
1
FWIW $('code').children()вернет пустой объект jQuery для приведенного выше примера, потому что codeэлементы не имеют узлов дочерних элементов. Хотя это работает, если есть дочерние элементы.
Феликс Клинг,
1
Исправлено - true, когда содержимое состоит из одного текстового узла, children()работать не будет. Использование contents()вместо этого работает отлично. jsfiddle.net/7Yne9
Йенс Роланд
1
Вот версия jsperf, которая не разбивает страницу: jsperf.com/substituting-one-tag-for-another-with-jquery/2, и вы видите, что разница в скорости уже не так велика . Ваше решение работает медленнее, потому что вы выполняете две манипуляции с DOM для каждого элемента: unwrapудаляет родителя и добавляет дочерние элементы к дереву, wrapснова отсоединяет их и добавляет нового родителя.
Феликс Клинг,
26

Это правильно, что вы всегда будете получать codeсодержимое первого , потому что$('code').html() что всегда будет ссылаться на первый элемент, где бы вы его ни использовали.

Вместо этого вы можете использовать .eachдля перебора всех элементов и изменения каждого по отдельности:

$('code').each(function() {
    $(this).replaceWith( "<pre>" + $(this).html() + "</pre>" );
    // this function is executed for all 'code' elements, and
    // 'this' refers to one element from the set of all 'code'
    // elements each time it is called.
});
pimvdb
источник
12

Попробуй это:

$('code').each(function(){

    $(this).replaceWith( "<pre>" + $(this).html() + "</pre>" );

});

http://jsfiddle.net/mTGhV/

Кокос
источник
Вот это да. вы, ребята, пришли к одному и тому же решению с интервалом в 2 секунды. есть небольшие различия в форматировании. Я не уверен, за какой из них проголосовать - мне действительно не нравится пробел между функцией и () в ответе Томаса, а две строки пустого пространства в ответе kokos довольно точны + между () должен быть пробел и {
Майкл Билстра
11

Как насчет этого?

$('code').each(function () {
    $(this).replaceWith( "<pre>" + $(this).html() + "</pre>" );
});
Тэ-Сун Шин
источник
6

Основываясь на ответе Феликса.

$('code').replaceWith(function() {
    var replacement = $('<pre>').html($(this).html());
    for (var i = 0; i < this.attributes.length; i++) {
        replacement.attr(this.attributes[i].name, this.attributes[i].value);
    }
    return replacement;
});

Это воспроизведет атрибуты codeтегов в заменеpre тегах .

Изменить: это заменит даже те codeтеги, которые находятся внутри innerHTMLдругих codeтегов.

function replace(thisWith, that) {
    $(thisWith).replaceWith(function() {
        var replacement = $('<' + that + '>').html($(this).html());
        for (var i = 0; i < this.attributes.length; i++) {
            replacement.attr(this.attributes[i].name, this.attributes[i].value);
        }
        return replacement;
    });
    if ($(thisWith).length>0) {
        replace(thisWith, that);
    }
}

replace('code','pre');
теватия
источник
2
лучший ответ на данный момент. Остальные почти все такие же, что действительно странно, что люди их поддерживают. Поздравляю, ты единственный, кто подумал об атрибутах.
Гильерме Давид да Коста
2

Если бы вы использовали ванильный JavaScript, вы бы:

  • Создайте новый элемент
  • Переместите дочерние элементы старого элемента в новый элемент
  • Вставьте новый элемент перед старым
  • Удалите старый элемент

Вот jQuery-эквивалент этого процесса:

$("code").each(function () {
    $("<pre></pre>").append(this.childNodes).insertBefore(this);
    $(this).remove();
});

Вот URL-адрес jsperf:
http://jsperf.com/substituting-one-tag-for-another-with-jquery/7

PS: Все решения, которые используют .html()или .innerHTMLявляются деструктивными .

Салман А
источник
2

Еще один короткий и простой способ:

$('code').wrapInner('<pre />').contents();
Фабиан фон Эллертс
источник
0
$('code').each(function(){
    $(this).replaceWith( "<pre>" + $(this).html()  + "</pre>" );
    });

Лучший и чистый способ.

Нимек
источник
0

Вы можете использовать функцию jQuery html. Ниже приведен пример замены тега кода на предварительный тег с сохранением всех атрибутов кода.

    $('code').each(function() {
        var temp=$(this).html();
        temp=temp.replace("code","pre");
        $(this).html(temp);
    });

Это может работать с любым набором тегов элементов html, которые необходимо поменять местами, с сохранением всех атрибутов предыдущего тега.

Арнаб К.
источник
0

Сделал jqueryплагин, поддерживающий также атрибуты.

$.fn.renameTag = function(replaceWithTag){
  this.each(function(){
        var outerHtml = this.outerHTML;
        var tagName = $(this).prop("tagName");
        var regexStart = new RegExp("^<"+tagName,"i");
        var regexEnd = new RegExp("</"+tagName+">$","i")
        outerHtml = outerHtml.replace(regexStart,"<"+replaceWithTag)
        outerHtml = outerHtml.replace(regexEnd,"</"+replaceWithTag+">");
        $(this).replaceWith(outerHtml);
    });
    return this;
}

Использование:

$('code').renameTag('pre')
Джагдиш Идхате
источник
0

Все приведенные здесь ответы предполагают (как показывает пример вопроса), что в теге нет атрибутов. Если принятый ответ дан на это:

<code class='cls'>A</code>

если будет заменен на

<pre>A</pre>

Что, если вы хотите сохранить и атрибуты - что будет означать замена тега ...? Это решение:

$("code").each( function(){
    var content = $( "<pre>" );

    $.each( this.attributes, function(){ 
         content.attr( this.name, this.value );
    } );

    $( this ).replaceWith( content );
} );
Патрик
источник
Я знаю, что этому пара лет, но я просто подумал, что упомяну ... вы забыли сохранить html из предыдущего элемента. Во фрагменте кода вы просто создаете новый элемент с атрибутами, не копируя при этом ни одного из дочерних элементов.
Друг
это будет сделано добавлениемcontent.html( this.html )
Патрик