So ändern Sie einen Elementtyp mit jquery

104

Ich habe den folgenden Code

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

Wie würde ich das bTag durch ein h1Tag ersetzen , aber alle anderen Attribute und Informationen beibehalten?

Bammab
quelle
@beanland: Die Attribute bleiben dabei nicht erhalten.
Felix Kling

Antworten:

137

Hier ist eine Möglichkeit, wie Sie dies mit jQuery tun können:

var attrs = { };

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


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

Beispiel: http://jsfiddle.net/yapHk/

Update , hier ist ein Plugin:

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

Beispiel: http://jsfiddle.net/mmNNJ/

Andrew Whitaker
quelle
2
@FelixKling: Danke, childrenhat nicht funktioniert, aber contentsfunktioniert.
Andrew Whitaker
1
@ Andrew Whitaker WOW !!! Du bist gut! Also nur um sicherzugehen, dass ich b.class oder b.xyzxterms benutze (xyzxterms ist der Name der Klasse)
bammab
5
@ AndrewWhitaker: Wenn ich mich nicht irre, werden in Ihrem Plugin die Attribute des ersten übereinstimmenden Elements auf alle übereinstimmenden Elemente angewendet. Es ist nicht unbedingt das, was wir wollen. Außerdem wird ein Fehler ausgelöst, wenn das Set kein übereinstimmendes Element enthält. Hier ist eine modifizierte Version Ihres Plugins, die eigene Attribute für jedes übereinstimmende Element beibehält
Etienne
2
Das funktioniert wie ein Zauber! Außer wenn der Selektor kein passendes Element findet, wird eine Fehlermeldung an die Konsole ausgegeben, da diese [0] undefiniert ist, wenn auf Attribute zugegriffen wird. Das Hinzufügen einer Bedingung behebt das Problem: if (this.length! = 0) {...
ciuncan
1
@ciuncan: Danke für das Feedback! Es sollte wirklich in einen .eachBlock eingewickelt werden , wie eine Antwort unten auch zeigt.
Andrew Whitaker
14

Ich bin mir bei jQuery nicht sicher. Mit einfachem JavaScript können Sie Folgendes tun:

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

DEMO

Ich bin mir nicht sicher, wie browserübergreifend dies ist.

Eine Variation könnte sein:

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

Weitere Informationen finden Sie unter Node.attributes [MDN] .

Felix Kling
quelle
Die Leistung Ihres Codes ist besser als die von "pure jQuery" ( z . B. Andrews Code), hat jedoch ein kleines Problem mit inneren Tags. Sehen Sie in diesem Beispiel Kursivschrift mit Ihrem Code und dem Referenzbeispiel .
Peter Krauss
Wenn Sie korrigieren, ein "ideales JQuery-Plugin" definiert werden, das Ihre Funktion über die JQuery-Plugin-Vorlage aufruft.
Peter Krauss
Fest. Das Problem war, dass nach dem Kopieren des ersten Kindes kein nächstes Geschwister mehr vorhanden ist, was while(child = child.nextSibling)fehlgeschlagen ist. Vielen Dank!
Felix Kling
9

@jakov und @Andrew Whitaker

Hier ist eine weitere Verbesserung, damit mehrere Elemente gleichzeitig verarbeitet werden können.

$.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);
};
Jazzbo
quelle
3

Die Antwort von @ Jazzbo gab ein jQuery-Objekt zurück, das ein Array von jQuery-Objekten enthielt, das nicht verkettbar war. Ich habe es so geändert, dass es ein Objekt zurückgibt, das dem ähnelt, was $ .each zurückgegeben hätte:

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

(Hat auch einige Code-Bereinigungen durchgeführt, damit jslint übergeben wird.)

fiskhandlarn
quelle
Dies scheint die schönste Option zu sein. Das einzige, was ich nicht verstehe, ist, warum Sie die var-Deklaration für attrs aus this.each () verschoben haben. Es funktioniert gut damit links: jsfiddle.net/9c0k82sr/1
Jacob C. sagt Reinstate Monica
Ich habe die Variablen wegen jslint gruppiert: "(Hat auch einige Code-Bereinigungen durchgeführt, damit sie jslint übergeben.)". Die Idee dahinter ist, den Code schneller zu machen, denke ich (ohne vars in jeder eachSchleife neu deklarieren zu müssen ).
Fiskhandlarn
2

Ich kann mir nur vorstellen, alles manuell zu kopieren: Beispiel 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);
});
Kasdega
quelle
2

@ Andrew Whitaker: Ich schlage diese Änderung vor:

$.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;
};

Dann können Sie Dinge tun wie: $('<div>blah</div>').changeElementType('pre').addClass('myclass');

jakov
quelle
2

Ich mag die Idee von @AndrewWhitaker und anderen, ein jQuery-Plugin zu verwenden - um das hinzuzufügen changeElementType() Methode . Aber ein Plugin ist wie eine Blackbox, egal wie der Code aussieht, wenn er klein ist und gut funktioniert ... Leistung ist also erforderlich und am wichtigsten als Code.

"Pure Javascript" hat eine bessere Leistung als jQuery: Ich denke, dass der Code von @ FelixKling eine bessere Leistung hat als der von @ AndrewWhitaker und anderen.


Hier ein "reines Javavascript" (und "reines DOM") Code, gekapselt in ein jQuery-Plugin :

 (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);
Peter Krauss
quelle
2

Hier ist eine Methode, mit der ich HTML-Tags in jquery ersetze:

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

});
Seth McCauley
quelle
2

Mit jQuery ohne über Attribute zu iterieren:

Das replaceElemVerfahren , das unten übernimmt old Tag, new Tagund contextund führt den Austausch erfolgreich:


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>

Viel Glück...

Akash
quelle
1
Ich mag deine Antwort! Warum? Weil ALLE anderen Antworten beim Versuch, etwas Einfaches wie das Konvertieren eines Ankers in ein Etikett zu tun, fehlschlagen. Beachten Sie jedoch bitte die folgenden Korrekturen / Überarbeitungen Ihrer Antwort: A). Ihr Code funktioniert nicht mit Selektoren. B) Ihr Code muss einen regulären Ausdruck ohne Berücksichtigung der Groß- und Kleinschreibung ausführen. Das heißt, hier sind meine vorgeschlagenen Korrekturen: regexOpeningTag = new RegExp ('^ <' + $ (el) .get (0) .tagName, 'i'); regexClosingTag = new RegExp ($ (el) .get (0) .tagName + '> $', 'i');
Zax
Wenn Sie einfaches HTML wie dieses ersetzen, verlieren Sie auch alle Ereignis-Listener, die an die Objekte angehängt sind.
José Yánez
1

Javascript-Lösung

Kopieren Sie die Attribute des alten Elements in das neue Element

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

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

Ersetzen Sie das alte Element durch das neue Element

$oldElem.parentNode.replaceChild($newElem, $oldElem)
svnm
quelle
maperstellt ein neues nicht verwendetes Array, das durch ersetzt werden könnte forEach.
Orkhan Alikhanov
1

Hier ist meine Version. Es handelt sich im Grunde genommen um die Version von @ fiskhandlarn, aber anstatt ein neues jQuery-Objekt zu erstellen, werden einfach die alten Elemente mit den neu erstellten überschrieben, sodass kein Zusammenführen erforderlich ist.
Demo: 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;
};
biziclop
quelle