如何使用jQuery更改元素类型

112

我有以下代码

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

如何将b标签替换为h1标签,但保留所有其他属性和信息?


1
请参见https://dev59.com/B3NA5IYBdhLWcg3wjOlS,该问题涉及使用jQuery更改HTML标记。 - Ian Hunter
@beanland:这不会保留属性。 - Felix Kling
11个回答

149

以下是使用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 起了作用。 - Andrew Whitaker
5
如果我没记错的话,在你的插件中,第一个匹配元素的属性会被应用到所有匹配元素上。这不一定是我们想要的。当集合中没有匹配元素时,还会产生错误。这是一个修改后的版本,它为每个匹配元素保留自己的属性,并且在空集合上不会触发错误:https://gist.github.com/2934516 - Etienne
2
这个功能非常好用!除了当选择器无法找到任何匹配的元素时,它会向控制台抛出错误消息,因为this[0]是未定义的,访问属性会出错。添加一个条件可以解决这个问题: if (this.length != 0) {... - ciuncan
1
@ciuncan:感谢您的反馈!就像下面的一个答案所示,它确实应该被包装在一个.each块中。 - Andrew Whitaker
1
有人能解释一下$ .each()的作用吗? - user4593252
显示剩余12条评论

17

我不确定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”(例如Andrew的代码)更好,但在内部标签方面存在一些问题,请参见此示例中使用您的代码参考示例中的斜体。 - Peter Krauss
如果你进行了修正,就可以定义一个“理想的jQuery插件”,通过jquery-plugin-template调用你的函数。 - Peter Krauss
已修复。问题在于在复制第一个子元素后,它没有下一个兄弟节点了,所以 while(child = child.nextSibling) 失败了。谢谢! - Felix Kling

10

这里有一个进一步的改进,使它可以同时处理多个元素。

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

4

@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测试。


这似乎是最好的选择。我唯一不明白的是为什么你把attrs的变量声明移到了this.each()之外。如果它留在那里,它也可以正常工作:http://jsfiddle.net/9c0k82sr/1/ - Jacob C.
我将变量分组是因为JSLint:“(还进行了一些代码清理,以便通过JSLint。)”背后的想法是使代码更快,我认为(不必在每个“each”循环中重新声明变量)。 - fiskhandlarn

2
我喜欢@AndrewWhitaker和其他人的想法,使用jQuery插件--添加changeElementType()方法。但是插件就像一个黑匣子,不管它的代码如何,如果它很小并且运行良好...因此,性能是必要的,比代码更重要。
"纯JavaScript"比jQuery具有更好的性能:我认为@FelixKling的代码比@AndrewWhitaker和其他人的代码性能更好。

这里是一个“纯JavaScript”(和“纯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

使用jQuery无需迭代属性:

下面的replaceElem方法接受旧标签新标签上下文,并成功执行替换:


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)您的代码无法使用选择器。B)您的代码需要执行不区分大小写的正则表达式。话虽如此,这是我的建议修复:regexOpeningTag = new RegExp('^<' + $(el).get(0).tagName,'i'); regexClosingTag = new RegExp($(el).get(0).tagName + '>$','i'); - zax
替换像这样的纯HTML也会使您失去附加到对象的任何事件侦听器。 - José Yánez

2
这是我在jQuery中用来替换HTML标签的方法:
// 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

@Andrew Whitaker:我提议进行以下更改:

$.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');

将代码中的div元素更改为pre元素,并添加myclass类。

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

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)

map 创建一个新的未使用的数组,可以被 forEach 替代。 - Orkhan Alikhanov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接