Use o jQuery para alterar uma tag HTML?

130

Isso é possível?

exemplo:

$('a.change').click(function(){
//code to change p tag to h5 tag
});


<p>Hello!</p>
<a id="change">change</a>

Portanto, clicar na âncora de alteração deve fazer com que a <p>Hello!</p>seção mude para (como exemplo) uma tag h5, para que você termine <h5>Hello!</h5>após o clique. Sei que você pode excluir a tag p e substituí-la por uma h5, mas existe alguma maneira de realmente modificar uma tag HTML?

Christopher Cooper
fonte

Respostas:

211

Depois que um elemento dom é criado, eu acredito que a tag é imutável. Você teria que fazer algo assim:

$(this).replaceWith($('<h5>' + this.innerHTML + '</h5>'));
mishac
fonte
2
Por favor, veja meu comentário abaixo ... alterar a estrutura do documento para aplicar estilo não é a melhor abordagem.
jrista
39
Isso não atrapalha nenhum atributo que possa estar no elemento que você substituiu? Poderia levar a um comportamento inesperado devido a atributos de estilo excluídos, atributos de dados, etc ...
Xavi
5
"<" + el.outerHTML.replace(/(^<\w+|\w+>$)/g, "H5") + ">";Função jQuery plugável ou plugável: link
basil
65

Aqui está uma extensão que fará tudo isso, em tantos elementos de várias maneiras ...

Exemplo de uso:

mantenha a classe e os atributos existentes:

$('div#change').replaceTag('<span>', true);

ou

Descartar a classe e os atributos existentes:

$('div#change').replaceTag('<span class=newclass>', false);

ou mesmo

substitua todas as divs por extensões, copie classes e atributos, adicione nome de classe extra

$('div').replaceTag($('<span>').addClass('wasDiv'), true);

Fonte do plugin:

$.extend({
    replaceTag: function (currentElem, newTagObj, keepProps) {
        var $currentElem = $(currentElem);
        var i, $newTag = $(newTagObj).clone();
        if (keepProps) {//{{{
            newTag = $newTag[0];
            newTag.className = currentElem.className;
            $.extend(newTag.classList, currentElem.classList);
            $.extend(newTag.attributes, currentElem.attributes);
        }//}}}
        $currentElem.wrapAll($newTag);
        $currentElem.contents().unwrap();
        // return node; (Error spotted by Frank van Luijn)
        return this; // Suggested by ColeLawrence
    }
});

$.fn.extend({
    replaceTag: function (newTagObj, keepProps) {
        // "return" suggested by ColeLawrence
        return this.each(function() {
            jQuery.replaceTag(this, newTagObj, keepProps);
        });
    }
});
Orwellophile
fonte
2
Sim. E, para o registro, na verdade existem muitos motivos válidos pelos quais você pode querer alterar uma tag. por exemplo, se você tivesse tags DIV em um SPAN, o que não é padrão. Eu aproveitei bastante essa função enquanto trabalhava com os rígidos padrões do princexml para publicação em pdb.
Orwellophile
1
Parece realmente bom, embora infelizmente perca todos os eventos do elemento substituído. Talvez isso pudesse ser resolvido também - seria incrível!
NPC
@ NPC é a vida na cidade grande. se você for substituir elementos, haverá baixas. eu tenho certeza que há alguém lá fora, quem sabe o jquery relevantes para clonar os eventos :)
Orwellophile
1
@FrankvanLuijn @orwellophile, na return node;verdade, deve ser return this;como mostrado na "versão antiga" do plug-in. Isso é essencial para encadear eventos como$("tr:first").find("td").clone().replaceTag("li").appendTo("ul#list")
Cole Lawrence
1
Não entendi, por que você precisa de 2 funções? Você precisa dos dois? Desculpe, estou perdido
João Pimentel Ferreira
12

Em vez de alterar o tipo de tag, você deve alterar o estilo da tag (ou melhor, a tag com um ID específico.) Não é uma boa prática alterar os elementos do seu documento para aplicar alterações estilísticas. Tente o seguinte:

$('a.change').click(function() {
    $('p#changed').css("font-weight", "bold");
});

<p id="changed">Hello!</p>
<a id="change">change</a>
jrista
fonte
3
Mesmo nesse caso, você não deve modificar a estrutura do documento. Se você precisar exibir uma entrada em resposta a um clique de edição, coloque a entrada e cole a tela: nenhuma ou visibilidade: oculta nela. Oculte <h5> e mostre a <input> em resposta ao clique no botão. Se você está constantemente modificando sua estrutura de documentos ... está apenas pedindo um monte de problemas de estilo e layout no caminho.
jrista
1
Absolutamente. Seu javascript está incorporado ou vinculado; portanto, se sua preocupação é segurança, sua abordagem à segurança é um pouco falha. Você deve processar o conteúdo do seu documento de acordo com a função do usuário ... misturar conteúdo para funções não é uma maneira segura de abordar o problema. Se alguém estiver conectado ao seu sistema como administrador, renderize o conteúdo para um administrador. Se eles estiverem conectados ao seu sistema como um leitor, renderize o conteúdo para um leitor. Isso elimina completamente o conteúdo que não deve ser acessado. Depois que o conteúdo for renderizado, use CSS para estilizar seu documento e mostrar / ocultar itens.
jrista
2
Eu concordo com você e levei suas recomendações para o projeto. Usarei toggle () para mostrar os elementos de administração que são renderizados apenas quando um administrador está logado. Embora não esteja relacionada diretamente à pergunta original, essa provavelmente é uma solução melhor do que a direção que eu estava seguindo originalmente. Felicidades!
Christopher Cooper
1
Woo! Ensacado outro blop de má segurança! Uma vitória e rodadas para todos! (insira o ícone da cerveja aqui)
jrista
1
Confiar no javascript do lado do cliente para segurança é realmente pior do que nenhuma segurança. Por quê? Porque você pensa que tem segurança, quando na verdade não tem.
BryanH
8

Percebi que a primeira resposta não era exatamente o que eu precisava, então fiz algumas modificações e achei que a postaria aqui.

Melhorado replaceTag(<tagName>)

replaceTag(<tagName>, [withDataAndEvents], [withDataAndEvents])

Argumentos:

  • tagName: String
    • O nome da tag, por exemplo, "div", "span" etc.
  • withDataAndEvents: Boolean
    • "Um booleano indicando se os manipuladores de eventos devem ser copiados junto com os elementos. A partir do jQuery 1.4, os dados dos elementos também serão copiados." informação
  • deepWithDataAndEvents: Boolean ,
    • Um booleano indicando se os manipuladores de eventos e os dados de todos os filhos do elemento clonado devem ser copiados. Por padrão, seu valor corresponde ao valor do primeiro argumento (cujo padrão é false). " Info

Devoluções:

Um elemento jQuery recém-criado

Ok, eu sei que existem algumas respostas aqui agora, mas resolvi escrever isso novamente.

Aqui, podemos substituir a tag da mesma maneira que usamos a clonagem. Estamos seguindo a mesma sintaxe que .clone () com o withDataAndEventse deepWithDataAndEventsque copiar as crianças dados e eventos nós, se usado.

Exemplo:

$tableRow.find("td").each(function() {
  $(this).clone().replaceTag("li").appendTo("ul#table-row-as-list");
});

Fonte:

$.extend({
    replaceTag: function (element, tagName, withDataAndEvents, deepWithDataAndEvents) {
        var newTag = $("<" + tagName + ">")[0];
        // From [Stackoverflow: Copy all Attributes](http://stackoverflow.com/a/6753486/2096729)
        $.each(element.attributes, function() {
            newTag.setAttribute(this.name, this.value);
        });
        $(element).children().clone(withDataAndEvents, deepWithDataAndEvents).appendTo(newTag);
        return newTag;
    }
})
$.fn.extend({
    replaceTag: function (tagName, withDataAndEvents, deepWithDataAndEvents) {
        // Use map to reconstruct the selector with newly created elements
        return this.map(function() {
            return jQuery.replaceTag(this, tagName, withDataAndEvents, deepWithDataAndEvents);
        })
    }
})

Observe que isso não substitui o elemento selecionado, ele retorna o recém-criado.

Cole Lawrence
fonte
2
Esteja ciente de que .children()não incluirá nenhum nó puramente de texto. Você pode tentar o .contents()IIRC.
Orwellophile
5

A idéia é quebrar o elemento e desembrulhar o conteúdo:

function renameElement($element,newElement){

    $element.wrap("<"+newElement+">");
    $newElement = $element.parent();

    //Copying Attributes
    $.each($element.prop('attributes'), function() {
        $newElement.attr(this.name,this.value);
    });

    $element.contents().unwrap();       

    return $newElement;
}

Uso da amostra:

renameElement($('p'),'h5');

Demo

Shubanker
fonte
0

Eu vim com uma abordagem na qual você usa uma representação de string do seu objeto jQuery e substitui o nome da tag usando expressões regulares e JavaScript básico. Você não perderá nenhum conteúdo e não precisará repetir cada atributo / propriedade.

/*
 * replaceTag
 * @return {$object} a new object with replaced opening and closing tag
 */
function replaceTag($element, newTagName) {

  // Identify opening and closing tag
  var oldTagName = $element[0].nodeName,
    elementString = $element[0].outerHTML,
    openingRegex = new RegExp("^(<" + oldTagName + " )", "i"),
    openingTag = elementString.match(openingRegex),
    closingRegex = new RegExp("(<\/" + oldTagName + ">)$", "i"),
    closingTag = elementString.match(closingRegex);

  if (openingTag && closingTag && newTagName) {
    // Remove opening tag
    elementString = elementString.slice(openingTag[0].length);
    // Remove closing tag
    elementString = elementString.slice(0, -(closingTag[0].length));
    // Add new tags
    elementString = "<" + newTagName + " " + elementString + "</" + newTagName + ">";
  }

  return $(elementString);
}

Por fim, você pode substituir o objeto / nó existente da seguinte maneira:

var $newElement = replaceTag($rankingSubmit, 'a');
$('#not-an-a-element').replaceWith($newElement);
kevinweber
fonte
0

Esta é a minha solução. Permite alternar entre tags.

<!DOCTYPE html>
<html>
<head>
	<title></title>

<script src="https://code.jquery.com/jquery-1.11.3.js"></script>
<script type="text/javascript">

function wrapClass(klass){
	return 'to-' + klass;
}

function replaceTag(fromTag, toTag){
	
	/** Create selector for all elements you want to change.
	  * These should be in form: <fromTag class="to-toTag"></fromTag>
	  */
	var currentSelector = fromTag + '.' + wrapClass(toTag);

	/** Select all elements */
	var $selected = $(currentSelector);

	/** If you found something then do the magic. */
	if($selected.size() > 0){

		/** Replace all selected elements */
		$selected.each(function(){

			/** jQuery current element. */
			var $this = $(this);

			/** Remove class "to-toTag". It is no longer needed. */
			$this.removeClass(wrapClass(toTag));

			/** Create elements that will be places instead of current one. */
			var $newElem = $('<' + toTag + '>');

			/** Copy all attributes from old element to new one. */
			var attributes = $this.prop("attributes");
			$.each(attributes, function(){
				$newElem.attr(this.name, this.value);
			});

			/** Add class "to-fromTag" so you can remember it. */
			$newElem.addClass(wrapClass(fromTag));

			/** Place content of current element to new element. */
			$newElem.html($this.html());

			/** Replace old with new. */
			$this.replaceWith($newElem);
		});

		/** It is possible that current element has desired elements inside.
		  * If so you need to look again for them.
		  */
		replaceTag(fromTag, toTag);
	}
}


</script>

<style type="text/css">
	
	section {
		background-color: yellow;
	}

	div {
		background-color: red;
	}

	.big {
		font-size: 40px;
	}

</style>
</head>
<body>

<button onclick="replaceTag('div', 'section');">Section -> Div</button>
<button onclick="replaceTag('section', 'div');">Div -> Section</button>

<div class="to-section">
	<p>Matrix has you!</p>
	<div class="to-section big">
		<p>Matrix has you inside!</p>
	</div>
</div>

<div class="to-section big">
	<p>Matrix has me too!</p>
</div>

</body>
</html>

zie1ony
fonte
0

Essa é a maneira rápida de alterar as tags HTML dentro do seu DOM usando o jQuery. Acho que essa função replaceWith () é muito útil.

   var text= $('p').text();
   $('#change').on('click', function() {
     target.replaceWith( "<h5>"+text+"</h5>" );
   });
Mahafuz
fonte
0

Você pode obter por data-*atributo data-replace="replaceTarget,replaceBy"dessa forma com a ajuda do jQuery para obter replaceTargete replaceByvalor por .split()método após obter valores e, em seguida, usar o .replaceWith()método
Essa data-*técnica de atributo para gerenciar facilmente qualquer substituição de tag sem alterar abaixo (código comum para toda a substituição de tag).

Espero que o trecho abaixo o ajude muito.

$(document).on('click', '[data-replace]', function(){
  var replaceTarget = $(this).attr('data-replace').split(',')[0];
  var replaceBy = $(this).attr('data-replace').split(',')[1];
  $(replaceTarget).replaceWith($(replaceBy).html($(replaceTarget).html()));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<p id="abc">Hello World #1</p>
<a href="#" data-replace="#abc,<h1/>">P change with H1 tag</a>
<hr>
<h2 id="xyz">Hello World #2</h2>
<a href="#" data-replace="#xyz,<p/>">H1 change with P tag</a>
<hr>
<b id="bold">Hello World #2</b><br>
<a href="#" data-replace="#bold,<i/>">B change with I tag</a>
<hr>
<i id="italic">Hello World #2</i><br>
<a href="#" data-replace="#italic,<b/>">I change with B tag</a>

Raeesh Alam
fonte
0

A função a seguir faz o truque e mantém todos os atributos. Você o usa por exemplo assim:changeTag("div", "p")

function changeTag(originTag, destTag) {
  while($(originTag).length) {
    $(originTag).replaceWith (function () {
      var attributes = $(this).prop("attributes");
      var $newEl = $(`<${destTag}>`)
      $.each(attributes, function() {
        $newEl.attr(this.name, this.value);
      });  
      return $newEl.html($(this).html())
    })
  }
}

Para ter certeza de que funciona, verifique o exemplo a seguir

function changeTag(originTag, destTag) {
  while($(originTag).length) {
    $(originTag).replaceWith (function () {
      var attributes = $(this).prop("attributes");
      var $newEl = $(`<${destTag}>`)
      $.each(attributes, function() {
        $newEl.attr(this.name, this.value);
      });  
      return $newEl.html($(this).html())
    })
  }
}

changeTag("div", "p")

console.log($("body").html())
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="A" style="font-size:1em">
  <div class="B" style="font-size:1.1em">A</div>
</div>
<div class="C" style="font-size:1.2em">
  B
</div>
</body>

João Pimentel Ferreira
fonte
-1

Existe um motivo específico para você precisar alterar a tag? Se você quiser apenas aumentar o texto, alterar a classe CSS da tag p seria a melhor maneira de fazer isso.

Algo assim:

$('#change').click(function(){
  $('p').addClass('emphasis');
});
Dave Ward
fonte
O motivo pelo qual estou pedindo para alterar o elemento / tag é porque estou tentando alterar uma tag (um <h5>, embora o tipo seja irrelevante) para um <input> quando um botão "editar" é clicado em outro lugar da página .
Christopher Cooper