comment changer un type d'élément en utilisant jquery

104

J'ai le code suivant

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

Comment remplacer la bbalise par une h1balise tout en conservant tous les autres attributs et informations?

bammab
la source
@beanland: Cela ne conserve pas les attributs.
Felix Kling

Réponses:

137

Voici une façon de le faire avec jQuery:

var attrs = { };

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


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

Exemple: http://jsfiddle.net/yapHk/

Mise à jour , voici un 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);

Exemple: http://jsfiddle.net/mmNNJ/

Andrew Whitaker
la source
2
@FelixKling: Merci, ça childrenn'a pas marché mais ça l'a contentsfait.
Andrew Whitaker
1
@Andrew Whitaker WOW !!! vous êtes doué! Donc, juste pour être sûr que j'utilise b.class ou b.xyzxterms (xyzxterms étant le nom de la classe)
bammab
5
@AndrewWhitaker: Si je ne me trompe pas, dans votre plugin, les attributs du premier élément correspondant seront appliqués à tous les éléments correspondants. Ce n'est pas nécessairement ce que nous voulons. Une erreur est également déclenchée lorsqu'il n'y a pas d'élément correspondant dans l'ensemble. Voici une version modifiée de votre plugin qui garde ses propres attributs pour chaque élément correspondant et ne déclenche pas d'erreur sur un ensemble vide: gist.github.com/2934516
Etienne
2
Ça fonctionne super bien! Sauf que lorsque le sélecteur ne parvient pas à trouver un élément correspondant, il envoie un message d'erreur à la console car ce [0] n'est pas défini. L'ajout d'une condition le corrige: if (this.length! = 0) {...
ciuncan
1
@ciuncan: Merci pour les commentaires! Il devrait vraiment être enveloppé dans un .eachbloc comme le montre une réponse ci-dessous.
Andrew Whitaker
14

Pas sûr de jQuery. Avec JavaScript brut, vous pouvez faire:

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

Je ne sais pas à quel point cela est compatible avec tous les navigateurs.

Une variation pourrait être:

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

Pour plus d'informations, voir Node.attributes [MDN] .

Félix Kling
la source
Les performances de votre code sont meilleures que le "pur jQuery" (par exemple le code d'Andrew), mais vous avez un petit problème avec les balises internes, voyez l'italique dans cet exemple avec votre code et l' exemple de référence .
Peter Krauss
Si vous corrigez, un "plugin jquery idéal" peut être défini, en appelant votre fonction par le jquery-plugin-template.
Peter Krauss
Fixé. Le problème était qu'après la copie sur le premier enfant, il n'avait plus de frère suivant, donc while(child = child.nextSibling)échoué. Merci!
Felix Kling
9

@jakov et @Andrew Whitaker

Voici une amélioration supplémentaire pour qu'il puisse gérer plusieurs éléments à la fois.

$.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
la source
3

La réponse de @ Jazzbo a renvoyé un objet jQuery contenant un tableau d'objets jQuery, qui n'étaient pas chaînables. Je l'ai changé pour qu'il renvoie un objet plus similaire à ce que $ .each aurait renvoyé:

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

(J'ai également nettoyé le code pour qu'il passe jslint.)

fiskhandlarn
la source
Cela semble être la meilleure option. La seule chose que je ne comprends pas, c'est pourquoi vous avez déplacé la déclaration var pour attrs hors de this.each (). Cela fonctionne bien avec cela laissé là: jsfiddle.net/9c0k82sr/1
Jacob C. dit Réintégrer Monica
J'ai regroupé les vars à cause de jslint: "(J'ai également fait du nettoyage de code pour qu'il passe jslint.)". L'idée derrière cela est de rendre le code plus rapide, je pense (ne pas avoir à redéclarer les variables dans chaqueeach boucle).
fiskhandlarn
2

La seule façon dont je peux penser est de tout copier manuellement: exemple 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
la source
2

@Andrew Whitaker: Je propose ce changement:

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

Ensuite, vous pouvez faire des choses comme: $('<div>blah</div>').changeElementType('pre').addClass('myclass');

Jakov
la source
2

J'aime l'idée de @AndrewWhitaker et d'autres, d'utiliser un plugin jQuery - pour ajouter le changeElementType() méthode. Mais un plugin est comme une boîte noire, peu importe le code, s'il est petit et fonctionne bien ... Donc, les performances sont requises, et sont plus importantes que le code.

"Pure javascript" a de meilleures performances que jQuery: je pense que le code de @ FelixKling a de meilleures performances que celui de @ AndrewWhitaker et d'autres.


Voici un code "pur Javavascript" (et "pur DOM"), encapsulé dans un plugin 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);
Peter Krauss
la source
2

Voici une méthode que j'utilise pour remplacer les balises html dans 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());
  });

});
Seth McCauley
la source
2

Avec jQuery sans itération sur les attributs:

La replaceElemméthode accepte ci - dessous old Tag, new Taget contextet exécute avec succès le remplacement:


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>

Bonne chance...

Akash
la source
1
J'aime ta réponse! Pourquoi? Parce que TOUTES les autres réponses échoueront en essayant de faire quelque chose de simple comme convertir une ancre en étiquette. Cela dit, veuillez considérer les correctifs / révisions suivants de votre réponse: A). Votre code ne fonctionnera pas avec les sélecteurs. B) Votre code doit exécuter une expression régulière insensible à la casse. Cela dit, voici mes suggestions de correctifs: regexOpeningTag = new RegExp ('^ <' + $ (el) .get (0) .tagName, 'i'); regexClosingTag = new RegExp ($ (el) .get (0) .tagName + '> $', 'i');
zax
Le remplacement du HTML brut comme celui-ci vous fera également perdre tous les écouteurs d'événements attachés aux objets.
José Yánez
1

Solution Javascript

Copiez les attributs de l'ancien élément dans le nouvel élément

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

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

Remplacez l'ancien élément par le nouvel élément

$oldElem.parentNode.replaceChild($newElem, $oldElem)
svnm
la source
mapcrée un nouveau tableau inutilisé, il pourrait être remplacé par forEach.
Orkhan Alikhanov
1

Voici ma version. Il s'agit essentiellement de la version de @ fiskhandlarn, mais au lieu de construire un nouvel objet jQuery, il écrase simplement les anciens éléments par ceux nouvellement créés, donc aucune fusion n'est nécessaire.
Démo: 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
la source