Utiliser jQuery pour modifier une balise HTML?

130

Est-ce possible?

exemple:

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


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

Donc, cliquer sur l'ancre de changement devrait entraîner le <p>Hello!</p>changement de la section (à titre d'exemple) en une balise h5 pour que vous vous retrouviez <h5>Hello!</h5>après le clic. Je réalise que vous pouvez supprimer la balise p et la remplacer par une h5, mais y a-t-il quand même pour modifier une balise HTML?

Christopher Cooper
la source

Réponses:

211

Une fois qu'un élément dom est créé, la balise est immuable, je crois. Vous devriez faire quelque chose comme ceci:

$(this).replaceWith($('<h5>' + this.innerHTML + '</h5>'));
mishac
la source
2
S'il vous plaît voir mon commentaire ci-dessous ... changer la structure du document pour appliquer le style n'est pas la meilleure approche.
jrista
39
Cela ne supprimerait-il pas les attributs qui pourraient être sur l'élément que vous avez remplacé? Pourrait conduire à un comportement inattendu en raison d'attributs de style supprimés, d'attributs de données, etc.
Xavi
5
"<" + el.outerHTML.replace(/(^<\w+|\w+>$)/g, "H5") + ">";Ou fonction jQuery enfichable: lien
basil
65

Voici une extension qui va tout faire, sur autant d'éléments et autant de manières ...

Exemple d'utilisation:

conserver la classe et les attributs existants:

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

ou

Supprimer la classe et les attributs existants:

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

ou même

remplacer toutes les div par des étendues, copier des classes et des attributs, ajouter un nom de classe supplémentaire

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

Source du 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
la source
2
Ouaip. Et pour mémoire, il existe en fait de nombreuses raisons très valables pour lesquelles vous voudrez peut-être changer une balise. par exemple, si vous aviez des balises DIV dans un SPAN, ce qui n'est pas standard. J'ai beaucoup utilisé cette fonction tout en travaillant avec les normes strictes de princexml pour la publication pdb.
Orwellophile
1
Cela a l'air vraiment bien, bien que malheureusement, il perd tous les événements de l'élément remplacé. Peut-être que cela pourrait être géré aussi - ce serait génial!
PNJ
@NPC, c'est la vie dans la grande ville. si vous remplacez des éléments, il y aura des victimes. je suis sûr qu'il y a quelqu'un là-bas qui connaît la jquery appropriée pour cloner les événements :)
Orwellophile
1
@FrankvanLuijn @orwellophile le return node;devrait en fait être return this;comme indiqué dans "l'ancienne version" du plugin. C'est essentiel pour enchaîner des événements comme$("tr:first").find("td").clone().replaceTag("li").appendTo("ul#list")
Cole Lawrence
1
Je ne comprends pas, pourquoi avez-vous besoin de 2 fonctions? Avez-vous besoin des deux? Désolé, je suis perdu
João Pimentel Ferreira le
12

Plutôt que de changer le type de balise, vous devriez changer le style de la balise (ou plutôt la balise avec un identifiant spécifique.) Ce n'est pas une bonne pratique de changer les éléments de votre document pour appliquer des changements stylistiques. Essaye ça:

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

<p id="changed">Hello!</p>
<a id="change">change</a>
jrista
la source
3
Même dans ce cas, vous ne devriez pas modifier la structure de votre document. Si vous avez besoin d'afficher une entrée en réponse à un clic sur un bouton d'édition, placez l'entrée et maintenez l'affichage: aucun ou visibilité: caché dessus. Cachez le <h5> et affichez le <input> en réponse au clic du bouton. Si vous modifiez constamment la structure de votre document ... vous demandez simplement un ensemble de problèmes de style et de mise en page.
jrista
1
Absolument. Votre javascript est intégré ou lié, donc si votre préoccupation est la sécurité, votre approche de la sécurité est quelque peu imparfaite. Vous devez rendre le contenu de votre document en fonction du rôle de l'utilisateur ... mélanger le contenu pour les rôles n'est pas un moyen sûr d'aborder le problème. Si quelqu'un est connecté à votre système en tant qu'administrateur, rendez le contenu pour un administrateur. S'ils sont connectés à votre système en tant que lecteur, rendez le contenu pour un lecteur. Cela élimine complètement le contenu qui ne devrait pas être consulté. Une fois le contenu rendu, utilisez CSS pour styliser votre document et afficher / masquer des éléments.
jrista
2
Je suis d'accord avec vous et j'ai intégré vos recommandations dans le projet. J'utiliserai toggle () pour afficher les éléments d'administrateur qui ne sont rendus que lorsqu'un administrateur est connecté. Bien que non lié (directement) à la question d'origine, c'est probablement une meilleure solution que la direction que j'allais à l'origine. À votre santé!
Christopher Cooper
1
Courtiser! J'ai mis un autre paquet de mauvaise sécurité! Une victoire et des manches pour tous! (insérer l'icône de la bière ici)
jrista
1
S'appuyer sur javascript côté client pour la sécurité est en fait PIRE que pas de sécurité du tout. Pourquoi? Parce que vous pensez avoir la sécurité, alors qu'en fait ce n'est pas le cas.
BryanH
8

J'ai remarqué que la première réponse n'était pas tout à fait ce dont j'avais besoin, alors j'ai fait quelques modifications et j'ai pensé que je la posterais ici.

Amélioré replaceTag(<tagName>)

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

Arguments:

  • tagName: Chaîne
    • Le nom de la balise, par exemple "div", "span", etc.
  • withDataAndEvents: Booléen
    • "Un booléen indiquant si les gestionnaires d'événements doivent être copiés avec les éléments. A partir de jQuery 1.4, les données des éléments seront également copiées." Info
  • deepWithDataAndEvents: Booléen ,
    • Une valeur booléenne indiquant si les gestionnaires d'événements et les données de tous les enfants de l'élément cloné doivent être copiés. Par défaut, sa valeur correspond à la valeur du premier argument (qui vaut par défaut false). " Info

Retour:

Un élément jQuery nouvellement créé

D'accord, je sais qu'il y a quelques réponses ici maintenant, mais j'ai pris l'initiative d'écrire ceci à nouveau.

Ici, nous pouvons remplacer la balise de la même manière que nous utilisons le clonage. Nous suivons la même syntaxe que .clone () avec withDataAndEventset deepWithDataAndEventsqui copient les données et les événements des nœuds enfants s'ils sont utilisés.

Exemple:

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

La source:

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

Notez que cela ne remplace pas l'élément sélectionné, il renvoie celui qui vient d'être créé.

Cole Lawrence
la source
2
Sachez que .children()cela n'inclura aucun nœud purement textuel. Vous voudrez peut-être essayer .contents()IIRC.
Orwellophile
5

L'idée est d'envelopper l'élément et de déballer le contenu:

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

Exemple d'utilisation:

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

Démo

Shubanker
la source
0

J'ai proposé une approche dans laquelle vous utilisez une représentation sous forme de chaîne de votre objet jQuery et remplacez le nom de la balise à l'aide d'expressions régulières et de JavaScript de base. Vous ne perdrez aucun contenu et n'avez pas à boucler sur chaque attribut / propriété.

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

Enfin, vous pouvez remplacer l'objet / nœud existant comme suit:

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

C'est ma solution. Il permet de basculer entre les balises.

<!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
la source
0

C'est le moyen rapide de modifier les balises HTML dans votre DOM en utilisant jQuery. Je trouve que cette fonction replaceWith () est très utile.

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

Vous pouvez obtenir par data-*attribut comme data-replace="replaceTarget,replaceBy"si avec l' aide de jQuery pour obtenir replaceTargetet replaceByvaleur par la .split()méthode après l' obtention de valeurs puis utilisez la .replaceWith()méthode.
Cet data-*attribut technique permet de gérer facilement tout remplacement de balise sans changer ci-dessous (code commun pour tout remplacement de balise).

J'espère que l'extrait ci-dessous vous aidera beaucoup.

$(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
la source
0

La fonction suivante fait l'affaire et conserve tous les attributs. Vous l'utilisez par exemple comme ceci: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())
    })
  }
}

Pour être sûr que cela fonctionne, consultez l'exemple suivant

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
la source
-1

Y a-t-il une raison spécifique pour laquelle vous devez changer le tag? Si vous voulez simplement agrandir le texte, changer la classe CSS de la balise p serait une meilleure façon de procéder.

Quelque chose comme ça:

$('#change').click(function(){
  $('p').addClass('emphasis');
});
Dave Ward
la source
La raison pour laquelle je demande de modifier l'élément / la balise est que j'essaie de changer une balise (un <h5>, bien que le type ne soit pas pertinent) en un <input> lorsqu'un bouton "modifier" est cliqué ailleurs sur la page .
Christopher Cooper