как изменить тип элемента с помощью jquery

105

У меня есть следующий код

<b class="xyzxterms" style="cursor: default; ">bryant keil bio</b>

Как мне заменить bтег на h1тег, но сохранить все остальные атрибуты и информацию?

баммаб
источник
1
См. Stackoverflow.com/questions/918792/…
Ян Хантер
@beanland: атрибуты не сохраняются.
Феликс Клинг

Ответы:

138

Вот один из способов сделать это с помощью jQuery:

var attrs = { };

$.each($("b")[0].attributes, function(idx, attr) {
    attrs[attr.nodeName] = attr.nodeValue;
});


$("b").replaceWith(function () {
    return $("<h1 />", attrs).append($(this).contents());
});

Пример: http://jsfiddle.net/yapHk/

Обновите , вот плагин:

(function($) {
    $.fn.changeElementType = function(newType) {
        var attrs = {};

        $.each(this[0].attributes, function(idx, attr) {
            attrs[attr.nodeName] = attr.nodeValue;
        });

        this.replaceWith(function() {
            return $("<" + newType + "/>", attrs).append($(this).contents());
        });
    };
})(jQuery);

Пример: http://jsfiddle.net/mmNNJ/

Эндрю Уитакер
источник
2
@FelixKling: Спасибо, childrenне сработало, но сработало contents.
Эндрю Уитакер
1
@ Эндрю Уитакер ВАУ !!! ты в порядке! Так что для уверенности я использую b.class или b.xyzxterms (xyzxterms - это имя класса)
bammab
5
@AndrewWhitaker: Если я не ошибаюсь, в вашем плагине атрибуты первого совпадающего элемента будут применяться ко всем совпадающим элементам. Это не обязательно то, что мы хотим. Также возникает ошибка, если в наборе нет соответствующего элемента. Вот модифицированная версия вашего плагина, которая сохраняет собственные атрибуты для каждого совпадающего элемента и не вызывает ошибку при пустом наборе: gist.github.com/2934516
Этьен,
2
Это работает как шарм! За исключением того, что когда селектор не может найти ни одного подходящего элемента, он выдает сообщение об ошибке на консоль, потому что это [0] не определено, доступ к атрибутам прерывается. Это исправляет добавление условия: if (this.length! = 0) {...
ciuncan
1
@ciuncan: Спасибо за отзыв! Он действительно должен быть заключен в .eachблок, как показано в ответе ниже.
Эндрю Уитакер
14

Не уверен насчет jQuery. С помощью простого JavaScript вы могли:

var new_element = document.createElement('h1'),
    old_attributes = element.attributes,
    new_attributes = new_element.attributes;

// copy attributes
for(var i = 0, len = old_attributes.length; i < len; i++) {
    new_attributes.setNamedItem(old_attributes.item(i).cloneNode());
}

// copy child nodes
do {
    new_element.appendChild(element.firstChild);
} 
while(element.firstChild);

// replace element
element.parentNode.replaceChild(new_element, element);

ДЕМО

Не уверен, насколько это совместимо с разными браузерами.

Вариантом может быть:

for(var i = 0, len = old_attributes.length; i < len; i++) {
    new_element.setAttribute(old_attributes[i].name, old_attributes[i].value);
}

Для получения дополнительной информации см. Node.attributes [MDN] .

Феликс Клинг
источник
Производительность вашего кода лучше, чем у «чистого jQuery» (например, кода Эндрю), но есть небольшая проблема с внутренними тегами, см. Курсив в этом примере с вашим кодом и справочным примером .
Питер Краусс
Если вы исправили, можно определить «идеальный плагин jquery» , вызывая вашу функцию с помощью шаблона jquery-plugin-template.
Питер Краусс
Исправлена. Проблема заключалась в том, что после копирования первого потомка у него больше не было следующего брата, поэтому while(child = child.nextSibling)не удалось. Спасибо!
Felix Kling
9

@jakov и @Andrew Whitaker

Вот еще одно улучшение, позволяющее обрабатывать сразу несколько элементов.

$.fn.changeElementType = function(newType) {
    var newElements = [];

    $(this).each(function() {
        var attrs = {};

        $.each(this.attributes, function(idx, attr) {
            attrs[attr.nodeName] = attr.nodeValue;
        });

        var newElement = $("<" + newType + "/>", attrs).append($(this).contents());

        $(this).replaceWith(newElement);

        newElements.push(newElement);
    });

    return $(newElements);
};
Джазбо
источник
3

Ответ @Jazzbo вернул объект jQuery, содержащий массив объектов jQuery, которые не были связаны. Я изменил его так, чтобы он возвращал объект, более похожий на то, что вернул бы $ .each:

    $.fn.changeElementType = function (newType) {
        var newElements,
            attrs,
            newElement;

        this.each(function () {
            attrs = {};

            $.each(this.attributes, function () {
                attrs[this.nodeName] = this.nodeValue;
            });

            newElement = $("<" + newType + "/>", attrs).append($(this).contents());

            $(this).replaceWith(newElement);

            if (!newElements) {
                newElements = newElement;
            } else {
                $.merge(newElements, newElement);
            }
        });

        return $(newElements);
    };

(Также была проведена некоторая очистка кода, чтобы он передавал jslint.)

Fiskhandlarn
источник
Кажется, это самый хороший вариант. Единственное, чего я не понимаю, - это почему вы переместили объявление var для attrs из this.each (). Он отлично работает, если оставить его там: jsfiddle.net/9c0k82sr/1
Джейкоб К. говорит: "Восстановите Монику"
Я сгруппировал вары из-за jslint: «(Также сделал некоторую очистку кода, чтобы он передавал jslint.)». Идея заключается в том, чтобы сделать код быстрее, я думаю (без необходимости повторно объявлять переменные в каждом eachцикле).
fiskhandlarn
2

Единственный способ, который я могу придумать, - это скопировать все вручную: пример jsfiddle

HTML

<b class="xyzxterms" style="cursor: default; ">bryant keil bio</b>

JQuery / Javascript

$(document).ready(function() {
    var me = $("b");
    var newMe = $("<h1>");
    for(var i=0; i<me[0].attributes.length; i++) {
        var myAttr = me[0].attributes[i].nodeName;
        var myAttrVal = me[0].attributes[i].nodeValue;
        newMe.attr(myAttr, myAttrVal);
    }
    newMe.html(me.html());
    me.replaceWith(newMe);
});
касдега
источник
2

@ Эндрю Уитакер: Я предлагаю это изменение:

$.fn.changeElementType = function(newType) {
    var attrs = {};

    $.each(this[0].attributes, function(idx, attr) {
        attrs[attr.nodeName] = attr.nodeValue;
    });

    var newelement = $("<" + newType + "/>", attrs).append($(this).contents());
    this.replaceWith(newelement);
    return newelement;
};

Затем вы можете делать такие вещи, как: $('<div>blah</div>').changeElementType('pre').addClass('myclass');

Яков
источник
2

Мне нравится идея @AndrewWhitaker и других использовать плагин jQuery - добавить changeElementType()метод. Но плагин похож на черный ящик, неважно код, если он мал и работает нормально ... Итак, производительность требуется, и она важнее кода.

«Чистый javascript» имеет лучшую производительность, чем jQuery: я думаю, что код @FelixKling имеет лучшую производительность, чем @ AndrewWhitaker и другие.


Вот код «чистого Javavascript» (и «чистого DOM»), инкапсулированный в плагин jQuery :

 (function($) {  // @FelixKling's code
    $.fn.changeElementType = function(newType) {
      for (var k=0;k<this.length; k++) {
       var e = this[k];
       var new_element = document.createElement(newType),
        old_attributes = e.attributes,
        new_attributes = new_element.attributes,
        child = e.firstChild;
       for(var i = 0, len = old_attributes.length; i < len; i++) {
        new_attributes.setNamedItem(old_attributes.item(i).cloneNode());
       }
       do {
        new_element.appendChild(e.firstChild);
       }
       while(e.firstChild);
       e.parentNode.replaceChild(new_element, e);
      }
      return this; // for chain... $(this)?  not working with multiple 
    }
 })(jQuery);
Питер Краусс
источник
2

Вот метод, который я использую для замены тегов html в jquery:

// Iterate over each element and replace the tag while maintaining attributes
$('b.xyzxterms').each(function() {

  // Create a new element and assign it attributes from the current element
  var NewElement = $("<h1 />");
  $.each(this.attributes, function(i, attrib){
    $(NewElement).attr(attrib.name, attrib.value);
  });

  // Replace the current element with the new one and carry over the contents
  $(this).replaceWith(function () {
    return $(NewElement).append($(this).contents());
  });

});
Сет МакКоли
источник
2

С jQuery без итерации атрибутов:

replaceElemНиже метод принимает old Tag, new Tagа contextи успешно выполняет замену:


replaceElem('h2', 'h1', '#test');

function replaceElem(oldElem, newElem, ctx) {
  oldElems = $(oldElem, ctx);
  //
  $.each(oldElems, function(idx, el) {
    var outerHTML, newOuterHTML, regexOpeningTag, regexClosingTag, tagName;
    // create RegExp dynamically for opening and closing tags
    tagName = $(el).get(0).tagName;
    regexOpeningTag = new RegExp('^<' + tagName, 'i'); 
    regexClosingTag = new RegExp(tagName + '>$', 'i');
    // fetch the outer elem with vanilla JS,
    outerHTML = el.outerHTML;
    // start replacing opening tag
    newOuterHTML = outerHTML.replace(regexOpeningTag, '<' + newElem);
    // continue replacing closing tag
    newOuterHTML = newOuterHTML.replace(regexClosingTag, newElem + '>');
    // replace the old elem with the new elem-string
    $(el).replaceWith(newOuterHTML);
  });

}
h1 {
  color: white;
  background-color: blue;
  position: relative;
}

h1:before {
  content: 'this is h1';
  position: absolute;
  top: 0;
  left: 50%;
  font-size: 5px;
  background-color: black;
  color: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


<div id="test">
  <h2>Foo</h2>
  <h2>Bar</h2>
</div>

Удачи...

Акаши
источник
1
Мне нравится твой ответ! Зачем? Потому что ВСЕ другие ответы потерпят неудачу при попытке сделать что-то простое, например преобразовать привязку в метку. Тем не менее, примите во внимание следующие исправления / изменения к вашему ответу: A). Ваш код не будет работать с селекторами. Б) Ваш код должен выполнять регулярное выражение без учета регистра. Тем не менее, вот мои предлагаемые исправления: regexOpeningTag = new RegExp ('^ <' + $ (el) .get (0) .tagName, 'i'); regexClosingTag = новое регулярное выражение ($ (el) .get (0) .tagName + '> $', 'i');
zax
Замена обычного HTML таким образом также приведет к потере любых прослушивателей событий, прикрепленных к объектам.
José Yánez
1

Решение Javascript

Скопируйте атрибуты старого элемента в новый элемент

const $oldElem = document.querySelector('.old')
const $newElem = document.createElement('div')

Array.from($oldElem.attributes).map(a => {
  $newElem.setAttribute(a.name, a.value)
})

Замените старый элемент новым элементом

$oldElem.parentNode.replaceChild($newElem, $oldElem)
svnm
источник
mapсоздает новый неиспользуемый массив, его можно заменить на forEach.
Орхан Алиханов
1

Вот моя версия. По сути, это версия @fiskhandlarn, но вместо создания нового объекта jQuery она просто перезаписывает старые элементы вновь созданными, поэтому слияние не требуется.
Демо: http://jsfiddle.net/0qa7wL1b/

$.fn.changeElementType = function( newType ){
  var $this = this;

  this.each( function( index ){

    var atts = {};
    $.each( this.attributes, function(){
      atts[ this.name ] = this.value;
    });

    var $old = $(this);
    var $new = $('<'+ newType +'/>', atts ).append( $old.contents() );
    $old.replaceWith( $new );

    $this[ index ] = $new[0];
  });

  return this;
};
бизиклоп
источник