cómo cambiar un tipo de elemento usando jquery

104

Tengo el siguiente código

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

¿Cómo reemplazaría la betiqueta por una h1etiqueta pero conservaría todos los demás atributos e información?

bammab
fuente
@beanland: Esto no conserva los atributos.
Felix Kling

Respuestas:

137

Aquí hay una forma en que puede hacerlo con jQuery:

var attrs = { };

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


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

Ejemplo: http://jsfiddle.net/yapHk/

Actualización , aquí hay un complemento:

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

Ejemplo: http://jsfiddle.net/mmNNJ/

Andrew Whitaker
fuente
2
@FelixKling: Gracias, childrenno funcionó, pero contentsfuncionó.
Andrew Whitaker
1
@ Andrew Whitaker ¡¡¡GUAU !!! ¡Estas bien! Entonces, solo para estar seguro de que uso b.class o b.xyzxterms (xyzxterms es el nombre de la clase)
bammab
5
@AndrewWhitaker: Si no me equivoco, en su complemento, los atributos del primer elemento coincidente se aplicarán a todos los elementos coincidentes. No es necesariamente lo que queremos. También se genera un error cuando no hay un elemento coincidente en el conjunto. Aquí hay una versión modificada de su complemento que mantiene sus propios atributos para cada elemento coincidente y no activa
Etienne
2
¡Esto funciona de maravilla! Excepto que cuando el selector no encuentra ningún elemento coincidente, arroja un mensaje de error a la consola porque este [0] no está definido para acceder a las rupturas de atributos. Agregar una condición lo corrige: if (this.length! = 0) {...
ciuncan
1
@ciuncan: ¡Gracias por los comentarios! Realmente debería estar envuelto en un .eachbloque como se muestra en la respuesta a continuación.
Andrew Whitaker
14

No estoy seguro de jQuery. Con JavaScript simple, podría hacer:

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

MANIFESTACIÓN

Sin embargo, no estoy seguro de qué tan compatible es esto con todos los navegadores.

Una variación podría ser:

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

Para obtener más información, consulte Node.attributes [MDN] .

Felix Kling
fuente
El rendimiento de su código es mejor que el "jQuery puro" (por ejemplo, el código de Andrew), pero tiene un pequeño problema con las etiquetas internas, vea la cursiva en este ejemplo con su código y el ejemplo de referencia .
Peter Krauss
Si lo corrige, se puede definir un "complemento jquery ideal" , llamando a su función mediante la plantilla jquery-plugin-template.
Peter Krauss
Fijo. El problema fue que después de copiar al primer hijo, ya no tiene un hermano siguiente, así que while(child = child.nextSibling)falló. ¡Gracias!
Felix Kling
9

@jakov y @Andrew Whitaker

Aquí hay una mejora adicional para que pueda manejar varios elementos a la vez.

$.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
fuente
3

La respuesta de @ Jazzbo devolvió un objeto jQuery que contenía una matriz de objetos jQuery, que no se podía encadenar. Lo cambié para que devuelva un objeto más similar a lo que $ .each habría devuelto:

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

(También hice una limpieza de código para que pase jslint).

fiskhandlarn
fuente
Esta parece la mejor opción. Lo único que no entiendo es por qué movió la declaración var para attrs fuera de this.each (). Funciona bien dejándolo allí: jsfiddle.net/9c0k82sr/1
Jacob
Agrupé las vars debido a jslint: "(También hice una limpieza de código para que pase jslint)". La idea detrás de eso es hacer que el código sea más rápido, creo (sin tener que volver a declarar vars dentro de cada eachciclo).
fiskhandlarn
2

La única forma en la que puedo pensar es copiar todo manualmente: ejemplo 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
fuente
2

@Andrew Whitaker: Propongo este cambio:

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

Entonces puedes hacer cosas como: $('<div>blah</div>').changeElementType('pre').addClass('myclass');

Jakov
fuente
2

Me gusta la idea de @AndrewWhitaker y otros, de usar un complemento jQuery, para agregar el changeElementType()método. Pero un complemento es como una caja negra, no importa el código, si es pequeño y funciona bien ... Por lo tanto, se requiere rendimiento y es más importante que el código.

"Javascript puro" tiene un mejor rendimiento que jQuery: Creo que el código de @ FelixKling tiene un mejor rendimiento que el de @ AndrewWhitaker y otros.


Aquí un código "puro Javavascript" (y "puro DOM"), encapsulado en un complemento de 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
fuente
2

Aquí hay un método que utilizo para reemplazar las etiquetas html en 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
fuente
2

Con jQuery sin iterar sobre los atributos:

El replaceElemsiguiente método acepta old Tag, new Tagy contexty ejecuta la sustitución con éxito:


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>

Buena suerte...

Akash
fuente
1
¡Me gusta tu respuesta! ¿Por qué? Porque TODAS las otras respuestas fallarán al intentar hacer algo simple como convertir un ancla en una etiqueta. Dicho esto, considere las siguientes correcciones / revisiones a su respuesta: A). Su código no funcionará con selectores. B) Su código debe realizar expresiones regulares que no distingan entre mayúsculas y minúsculas. Dicho esto, aquí están mis soluciones sugeridas: regexOpeningTag = new RegExp ('^ <' + $ (el) .get (0) .tagName, 'i'); regexClosingTag = new RegExp ($ (el) .get (0) .tagName + '> $', 'i');
zax
Reemplazar HTML simple como este también le hará perder cualquier detector de eventos adjunto a los objetos.
José Yánez
1

Solución javascript

Copie los atributos del elemento antiguo al nuevo elemento

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

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

Reemplazar el elemento antiguo con el nuevo elemento

$oldElem.parentNode.replaceChild($newElem, $oldElem)
svnm
fuente
mapcrea una nueva matriz sin usar, podría ser reemplazada por forEach.
Orkhan Alikhanov
1

Aquí está mi versión. Es básicamente la versión de @ fiskhandlarn, pero en lugar de construir un nuevo objeto jQuery, simplemente sobrescribe los elementos antiguos con los recién creados, por lo que no es necesario fusionarlos.
Demostración: 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
fuente