InnerHTML: Comment éviter

J'écris un plugin qui convertira les émoticônes en images dans un bloc de texte pour un site particulier. La réponse facile consiste à utiliser des expressions régulières pour détecter le texte de déclenchement sur innerHTML et insérer la balise img, puis faire passer la chaîne à l'élément dom dans la partie innerHTML . Le bloc d'élément DOM peut déjà avoir l'ancrage <a> et / ou le formatage du texte <b>,<i>,<u> dans la partie innerHTML.

Par exemple

  var textBlock = pItems[i].innerHTML; var kissSource = 'https://mail.google.com/mail/e/35D'; textBlock = textBlock.replace(/(^|[^&lt;]|[^&gt;]):\*/g, "<img class='emoticon' src='" + kissSource + "' />"); --> pItems[i].innerHTML = textBlock; //<-can I avoid this to be in compliance with Mozilla addons reqmnts 

J'ai cherché pendant quelques heures sur la façon dont je pourrais convertir un DOM (ou une chaîne HTML) en XML (je sais: HTML! = XLM Comment lire HTML comme XML? Mais peut-être une partie d'un div?) Qui à son tour pourrait être Converti en JSON qui à son tour … pourrait être converti en un DOM suivant: https://developer.mozilla.org/en-US/docs/XUL/School_tutorial/DOM_Building_and_HTML_Insertion?redirectlocale=fr-FR&redirectslug=XUL_School%2FDOM_Building_and_HTML_Insertion#JSON_Templating

Je ne connais pas jQuery, alors s'il vous plaît, s'il est possible avec javascript, ce serait idéal.

Devrais-je résoudre ce problème sans exécuter l'expression régulière contre le innerHTML, évitant ainsi le problème?

Merci beaucoup, JennaS

Solution de travail

Selon votre commentaire récent à @Giuseppe, et votre style mal formé que vous avez emprunté à ma publication, la seule solution serait d'éviter la récurrence ou l'itération à travers les chaînes de texte à la recherche d'éléments correspondants pour votre regEx.

  1. Appliquez le regex à votre chaîne comme vous le proposez.
  2. Une fois terminé, créez un DOM à partir de cette chaîne à l'aide d'un analyseur de chaîne HTMLify
  3. Remplacez le nœud par le nouveau noeud DOM créé à partir de la chaîne.

NB: Ceci est également utile lorsque vous tirez sur la page HTML AJAX et que vous devez analyser les résultats HTML dans un objet DOM temporaire, mais ne veut pas simplement jeter le contenu dans le innerHTML d'un élément nouvellement créé. Notez également que l'utilisation de createDocumentFragment ne convient pas car vous ne pouvez pas parcourir un fragment comme un arbre DOM.

Les étapes sont difficiles, mais il y a quelques postes géniaux sur Stackoverflow qui le rendent facile!
Après avoir fait des recherches pour vous et en cours d'exécution dans une solution maintenant obsolète et des analyseurs de code qui ne fonctionnera pas pour vous, je suis tombé sur une solution de @ rob-w: un analyseur de code

Votre code inclura l'analyseur DOM à partir du lien @ rob-w plus:

  /* * DOMParser HTML extension * 2012-02-02 * * By Eli Grey, http://eligrey.com * Public domain. * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. */ /*! @source https://gist.github.com/1129031 */ /*global document, DOMParser*/ (function (DOMParser) { "use strict"; var DOMParser_proto = DOMParser.prototype; var real_parseFromString = DOMParser_proto.parseFromString; // Firefox/Opera/IE throw errors on unsupported types try { // WebKit returns null on unsupported types if ((new DOMParser).parseFromString("", "text/html")) { // text/html parsing is natively supported return; } } catch (ex) {} DOMParser_proto.parseFromString = function (markup, type) { if (/^\s*text\/html\s*(?:;|$)/i.test(type)) { var doc = document.implementation.createHTMLDocument(""); var doc_elt = doc.documentElement; var first_elt; doc_elt.innerHTML = markup; first_elt = doc_elt.firstElementChild; if (doc_elt.childElementCount === 1 && first_elt.localName.toLowerCase() === "html") { doc.replaceChild(first_elt, doc_elt); } return doc; } else { return real_parseFromString.apply(this, arguments); } }; }(DOMParser)); autostyle = function (str) { var boldPattern = /(?![^<]*<\/a>)(^|<.>|[\s\W_])\*(\S.*?\S)\*($|<\/.>|[\s\W_])/g; var italicsPattern = /(?![^<]*<\/a>)(^|<.>|[\s\W])_(\S.*?\S)_($|<\/.>|[\s\W])/g; var strikethroughPattern = /(?![^<]*<\/a>)(^|<.>|[\s\W_])-(\S.*?\S)-($|<\/.>|[\s\W_])/gi; var underlinePattern = /(?![^<]*<\/a>)(^|<.>|[\s\W_])!(\S.*?\S)!($|<\/.>|[\s\W_])/gi; str = str.replace(strikethroughPattern, '$1<s>$2</s>$3'); str = str.replace(italicsPattern, '$1<i>$2</i>$3'); str = str.replace(boldPattern, '$1<b>$2</b>$3'); str = str.replace(underlinePattern, '$1<u>$2</u>$3'); return str; }; emoticonRegexFunction = function(str) { //do something return str; } RegexWithoutInnerHTML = function () { pItems = document.getElementsByTagName('p'); for (var k = 0; k < pItems.length; k++) { var str = pItems[k].textContent; str = autostyle(str); str = emoticonRegexFunction(str); var doc = new DOMParser().parseFromString('<p>' + str + '</p>', 'text/html'); pItems[k].parentNode.replaceChild(doc.getElementsByTagName('p')[0], pItems[k]); // pItems[k].innerHTML = str; //<-now do not need innerHTML } }; 

Exemple complet de travail sur jsbin à l'adresse suivante: http://jsbin.com/itiwek/12/edit

Prendre plaisir.

Si je vous comprends bien, vous voulez convertir ":)" à une émoticône. Pour ce faire, vous devez analyser le texte et remplacer ces caractères par un élément img. Donc, vous voulez faire quelque chose comme:

 <script> function toArray(collection) { var arr = []; for (var i=0, iLen=collection.length; i<iLen; i++) { arr[i] = collection[i]; } return arr; } function replaceTextWithEmoticon(el) { var re0 = /\:\)/g; var emo0 = document.createElement('img'); emo0.src = 'https://mail.google.com/mail/e/35D'; var node, txt, frag; var nodes = toArray(el.childNodes); for (var i=0, iLen=nodes.length; i<iLen; i++) { node = nodes[i]; // If it's an element node, process the content if (node.nodeType == 1) { replaceTextWithEmoticon(node); // If it's a text node, look for matching text and replace with emoticon } else if (node.nodeType == 3 && re0.test(node.data)) { txt = node.data.split(re0); frag = document.createDocumentFragment(); for (var j=0, jLen=txt.length - 1; j<jLen; j++) { frag.appendChild(document.createTextNode(txt[j])); frag.appendChild(emo0.cloneNode(false)); } if (txt[j] && txt[j].length) { frag.appendChild(document.createTextNode(txt[j])); } } node.parentNode.replaceChild(frag, node); } } </script> <p id="p0">here is a smily:) and another two:):)</p> <button onclick=" replaceTextWithEmoticon(document.getElementById('p0')); ">Replace with emoticon</button> 

Ce qui précède doit être modifié pour traiter plusieurs émoticônes différentes dans chaque noeud de texte, mais je pense que vous avez l'idée.

JsFiddle Demo

Profitez des outils disponibles. Vous pouvez naviguer vers le bas du DOM à partir de l'élément que vous avez déjà stocké dans les éléments. La première image tag, vous allez modifier la src. Cela fonctionnera:

 pItems[i].getElementsByTagName("img")[0].src = kissSource; 

Je ne suis pas sûr de comment commenter les publications de l'autre, mais si vous ajoutez un filtre à la réponse de @RobG, cela vous amènera là-bas. (PS: J'ai envoyé une suggestion d'édition de code)

 .... var nodes = toArray(el.childNodes); if (node.nodeName != "#text") //will allow embedded <b> <img> <a> etc to work. continue; for (var i=0, iLen=nodes.length; i<iLen; i++) { .... <p id="p0">here is a smily:) and <b>another</b> <a href="www.google.com">google</a> two:):) <i>italics</i></p> .... 

Ou si vous souhaitez rechercher expressément certains éléments à sauter tels que <B> ou <IMG> ou <A> etc.

C'est le même problème que je dois obtenir l'examen complet sur mon Addon ( ImageZone ).

Je pense que ce n'est pas une bonne idée pour vous d'utiliser innerHTML, car vous allez perdre tous les auditeurs d'événements enregistrés sur le noeud cible. Je pense que vous pourriez utiliser un code comme celui-ci:

 var treeWalker = document.createTreeWalker( node, NodeFilter.SHOW_ALL, { acceptNode:function (node) { return node.nodeType == Node.TEXT_NODE ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT; }},false); or just var treeWalker = document.createTreeWalker( node, NodeFilter.SHOW_TEXT, { acceptNode:function (node) {return NodeFilter.FILTER_ACCEPT; }},false); while(treeWalker.nextNode()) { var n=walker.currentNode; var text=n.nodeValue; var a= text.split(/(--- your emoticon regexp code ---))/g); if (a.length > 1){ n.insertAfter(document.createTextNode(a[0])); var img=document.createElement("img"); switch (a[1]){ case '...': img.setAttribute('src','...'); break; } // or img.setAttribute('src',emos_srcs[a[1]]); n.insertAfter(img); n.insertAfter(document.createTextNode(a[2])); n.parentNode.removeChild(n); } }