innerHTML: Как избежать

Я пишу плагин, который преобразует смайлики в изображения в текстовом блоке для конкретного сайта. Простой ответ-использовать регулярные выражения для обнаружения текста триггера на innerHTML и вставки тега img, а затем передать строку обратно в элемент dom в innerHTML часть. Блок элементов DOM может уже иметь anchor <a> и/или форматирования текста <b>,<i>,<u> в части innerHTML.

    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

Я искал совсем несколько часов о том, как я могу преобразовать DOM (или HTML-строку) в XML (я знаю: HTML!=В XLM Как читать HTML как XML? но, возможно, часть div?) который в свою очередь может быть преобразован в JSON, который в свою очередь .. может быть преобразован обратно в DOM: https://developer.mozilla.org/en-US/docs/XUL/School_tutorial/DOM_Building_and_HTML_Insertion?redirectlocale=en-US&redirectslug=XUL_School%2FDOM_Building_and_HTML_Insertion#JSON_Templating

Я не знайте jQuery, поэтому, пожалуйста, если это возможно с javascript, который был бы идеальным.

должен ли я решать эту проблему без запуска регулярного выражения против innerHTML, таким образом, избегая проблемы?

большое спасибо, Дженнас

5 ответов


Рабочего Раствора

на основе вашего недавнего комментария к @Giuseppe и вашего искаженного стиля, который вы заимствовали из моего поста, единственным решением было бы избежать рекурсии или итерации по текстовым строкам, ищущим соответствующие элементы для вашего регулярного выражения.

  1. применить регулярное выражение в строку, как вы предлагаете.
  2. после завершения сборки DOM из этой строки с помощью синтаксического анализатора строки HTMLify
  3. замените узел на новый DOM узел, построенный из строки.

NB: это также полезно при вытягивании страницы AJAX HTML, и вам нужно проанализировать результаты HTML во временном объекте DOM, но не хотите просто сбрасывать содержимое в innerHTML недавно созданного элемента. Также обратите внимание, что использование createDocumentFragment не подходит, поскольку вы не можете перемещаться по фрагменту как дерево DOM.

шаги звучат трудно, но есть несколько больших сообщений на Stackoverflow, которые делают его полегче!
После проведения исследований для вас и работает в теперь устаревшее решение и DOM парсеры, которые не будут работать для вас я наткнулся на решение от @rob-w:парсер dom

ваш код будет включать парсер DOM из @ 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, '<s></s>');
         str = str.replace(italicsPattern, '<i></i>');
         str = str.replace(boldPattern, '<b></b>');
         str = str.replace(underlinePattern, '<u></u>');
         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
         }
     };

полный рабочий пример на jsbin по адресу:http://jsbin.com/itiwek/12/edit

наслаждайтесь.


Если я правильно вас понимаю, вы хотите преобразовать say ":)" в смайлик. Для этого вам нужно разобрать текст и заменить эти символы элементом img. Поэтому вы хотите сделать что-то вроде:

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

выше необходимо изменить, чтобы иметь дело с несколькими различными смайликами в каждом текстовом узле, но я думаю, вы поняли идею.


демо jsFiddle

воспользуйтесь преимуществами доступных инструментов. Вы можете перемещаться по DOM из элемента, который вы уже сохранили в pItems. Первый тег изображения, вы измените src. Это сработает:

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

Я не уверен, как комментировать сообщения других, но если вы добавите фильтр к ответу @RobG, он доставит вас туда. (PS: Я представил предложение по редактированию кода)

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

или если вы хотите явно искать определенные элементы, чтобы пропустить такие как <B> или <IMG> или <A> etc.


Это тот же вопрос, который я должен получить полный обзор моего аддона ( ImageZone).

Я думаю, что вам не стоит использовать innerHTML, так как вы потеряете все прослушиватели событий, зарегистрированные на узле targets. Я думаю, вы можете использовать такой код:

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

 }