innerHTML: How To Avoid

前端 未结 5 426
孤城傲影
孤城傲影 2021-01-02 16:00

I\'m writing a plugin which will convert emoticons to images in a text block for a particular site. The easy answer is to use regular expressions to detect the trigger text

相关标签:
5条回答
  • 2021-01-02 16:42

    I'm not sure how to comment on other's posts but if you add a filter to @RobG response it will get you there. (PS: I did submit a code edit suggestion)

    ....
    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>
        ....
    

    Or if you want to expressly look for certain elements to skip such as <B> or <IMG> or <A> etc.

    0 讨论(0)
  • 2021-01-02 16:47

    If I understand you correctly, you want to convert say ":)" to an emoticon. To do that, you need to parse the text and replace those characters with an img element. So you want to do something like:

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

    The above needs to be modified to deal with multiple different emoticons in each text node, but I think you get the idea.

    0 讨论(0)
  • 2021-01-02 16:51

    This is the same issue I have to obtain the full review on my Addon (ImageZone).

    I think is not a good idea for you to use innerHTML, since you will lost all event listeners registered on the targets node. I think you could use a code like this:

    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);
         }
    
     }
    
    0 讨论(0)
  • 2021-01-02 16:54

    Working Solution

    Based on your recent comment to @Giuseppe, and your malformed styling that you borrowed from my post the only solution would be to avoid recursion or iterating through the text strings looking for matching elements for your regEx.

    1. Apply the regex to your string as you are proposing.
    2. Once finished build a DOM from that string using a HTMLify string parser
    3. Replace the node with the new DOM node built from the string.

    NB: This is also useful when pulling in AJAX HTML page and you need to parse the HTML results in a temporary DOM object but don't want to merely dump the contents into a the innerHTML of a newly created element. Also note that using createDocumentFragment will not be suitable as you cannot navigate a fragment like a DOM tree.

    The steps sound hard but there are a few great posts on Stackoverflow which make it easy!
    After doing research for you and running into a now obsolete solution and dom parsers which won't work for you I came across a solution from @rob-w: a dom parser

    Your code would include the DOM parser from @rob-w link 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
             }
         };
    

    Full working example on jsbin at: http://jsbin.com/itiwek/12/edit

    Enjoy.

    0 讨论(0)
  • 2021-01-02 16:55

    jsFiddle Demo

    Take advantage of the tools available. You can navigate down the DOM from the element you already have stored in pItems. The first image tag, you will alter the src. This will work:

    pItems[i].getElementsByTagName("img")[0].src = kissSource;
    
    0 讨论(0)
提交回复
热议问题