jQuery / Cambiar una etiqueta HTML

Para cambiar una etiqueta HTML con jQuery, cree un elemento del tipo deseado y sustituya el anterior. Asignar tagName no renombra el elemento. Para conservar valores y eventos de los hijos, mueva los nodos reales antes de eliminar el contenedor, en lugar de copiar una cadena HTML.

Nombre de etiqueta y sustitución de elemento

El DOM, Document Object Model, representa el documento mediante objetos nodo. Element.tagName es de solo lectura. .attr("tagName", "section") no cambia el tipo; .prop() tampoco permite reescribir esta propiedad.

Cree section con document.createElement() y sustituya div con .replaceWith(). El objeto exterior es nuevo aunque parezca cambiar solo el nombre. Las variables que apuntaban a div no pasan automáticamente a section.

Este ejercicio compara estados durante esa sustitución; no es un diseño semántico completo de una sección independiente. En un sitio, elija section según contenido y estructura de títulos. Para un cambio solo visual, class y CSS pueden resultar más apropiados.

Ejemplo completo que conserva los hijos

Guarde todo en UTF-8 como tag-demo.html y ábralo en el navegador. Internet carga el archivo oficial de jQuery 4.0.0. No requiere servidor ni paquetes. Los comportamientos principales se ejecutaron en Edge para Windows con 3.7.1 y 4.0.0.

div contiene texto, comentario, campo y botón. El botón tiene un controlador directo, main delegación y div sus propios datos y controlador. Los contadores separados permiten comparar qué se conserva.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Replace a container tag, keep its children</title>
  <script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
  <style>
    body { font: 16px/1.6 sans-serif; margin: 20px; max-width: 760px; }
    button, input { font: inherit; max-width: 100%; box-sizing: border-box; }
    button { margin: 4px; }
    .card { border: 3px solid teal; padding: 16px; margin: 16px 0; }
    #result { white-space: pre-wrap; overflow-wrap: anywhere; }
  </style>
</head>
<body>
  <button type="button" id="change">Change div to section</button>
  <button type="button" id="reset">Reset</button>
  <main id="board"></main>
  <pre id="result" aria-live="polite"></pre>
  <script>
    $(function () {
      let original, originalInput, originalNodes;
      let childClicks = 0, wrapperClicks = 0, delegatedClicks = 0;
      function report() {
        const card = document.getElementById("card");
        const sameChildren = originalNodes.every((node, i) => card.childNodes[i] === node);
        $("#result").text(`tag=${card.localName}\nsame wrapper=${card === original}\nsame children=${sameChildren}\nsame input=${card.querySelector("input") === originalInput}\ninput=${originalInput.value}\nold data=${$(original).data("lesson")}\nchild=${childClicks}; wrapper=${wrapperClicks}; delegated=${delegatedClicks}`);
      }
      function reset() {
        $("#board").empty();
        const card = document.createElement("div");
        card.id = "card"; card.className = "card";
        card.setAttribute("aria-label", "Editable note");
        card.append(document.createTextNode("Keep this text. "), document.createComment("keep this comment"));
        const input = document.createElement("input");
        input.type = "text"; input.value = "First note"; input.setAttribute("aria-label", "Note");
        const button = document.createElement("button");
        button.type = "button"; button.className = "inspect"; button.textContent = "Inspect";
        card.append(input, button); document.getElementById("board").append(card);
        original = card; originalInput = input; originalNodes = Array.from(card.childNodes);
        childClicks = wrapperClicks = delegatedClicks = 0;
        $(card).data("lesson", "old wrapper").on("click", function () { wrapperClicks++; });
        $(button).on("click", function () { childClicks++; });
        $(input).on("input", report);
        $("#change").prop("disabled", false); report();
      }
      $("#board").on("click.tagLesson", ".inspect", function () { delegatedClicks++; report(); });
      $("#change").on("click", function () {
        const old = document.getElementById("card");
        const replacement = document.createElement("section");
        for (const name of ["id", "class", "aria-label"]) {
          if (old.hasAttribute(name)) replacement.setAttribute(name, old.getAttribute(name));
        }
        // Move existing children BEFORE jQuery removes the old wrapper.
        $(replacement).append($(old).contents());
        $(old).replaceWith(replacement);
        $(this).prop("disabled", true); report();
      });
      $("#reset").on("click", reset);
      reset();
    });
  </script>
</body>
</html>

Salida inicial. same children comprueba identidad y orden de texto, comentario y elementos.

tag=div
same wrapper=true
same children=true
same input=true
input=First note
old data=old wrapper
child=0; wrapper=0; delegated=0
  1. Edite First note. Incluso <em>Edited note</em> permanece como valor, sin interpretarse como HTML.
  2. Un Inspect muestra child=1, wrapper=1, delegated=1: responden botón, div anterior y main.
  3. Change div to section muestra tag=section, same wrapper=false, same children=true y same input=true. Se conserva la edición; old data es undefined.
  4. Otro Inspect muestra child=2, wrapper=1, delegated=2. Se conservan el evento hijo y la delegación; el evento de div no se transfiere.
  5. Reset recrea div, valor inicial y contadores cero. Descarta las ediciones para iniciar otra comparación.

Conservación y configuración explícita

Ejecute $(replacement).append($(old).contents()) antes de sustituir. .contents() incluye hijos inmediatos de tipo elemento, texto y comentario. append mueve objetos existentes y conserva el value actual y los controladores de los hijos. .children() solo omitiría texto y comentarios fuera de los elementos.

Después .replaceWith() elimina el div vacío y limpia sus datos y eventos jQuery. Sustituir primero y recuperar hijos después puede perder su estado durante esa limpieza. No es una preservación universal: revise aparte foco, selección de texto y referencias internas de plugins.

El ejemplo copia explícitamente id, class y aria-label. Otros atributos no se transfieren solos. Copiar todos puede llevar atributos inválidos, roles obsoletos o eventos en línea al nuevo elemento. Elija los necesarios y revise su significado accesible. jQuery .data() y atributos HTML data-* son almacenamientos distintos.

.replaceWith() devuelve la colección jQuery original eliminada, no el nuevo elemento. Use replacement o vuelva a seleccionar el actual. Si necesita el comportamiento del contenedor, configúrelo explícitamente en el nuevo o delegue desde un antecesor estable.

Límites y diagnóstico

Leer $(old).html() y asignarlo con .html() vuelve a analizar una cadena. La misma apariencia no conserva objetos hijos, referencias, controladores directos ni valores actuales de formulario. Evite construir HTML de sustitución concatenando entradas del usuario.

Son contenedores HTML normales, no un conversor universal para input o img sin hijos, estructuras especiales de tablas, controles de formulario, SVG o elementos personalizados. Revise hijos permitidos y semántica. Cambiar button por a modifica también rol y uso del teclado.

Si cambia el aspecto, revise CSS dependiente de etiqueta como div.card y selectores del tipo anterior. Mantenga id único y preserve etiquetas y anclas. Compruebe eventos directos, delegación, foco, significado accesible y recuperación en la página real.