javascript - color - هل يمكنني تحميل مستند HTML بأكمله إلى جزء مستند في Internet Explorer؟



css in javascript (5)

إذا لم تكن متأكدًا من سبب العبث في المستندات documentFragments ، فيمكنك فقط تعيين نص HTML باعتباره innerHTML لعنصر div جديد. بعد ذلك ، يمكنك استخدام عنصر div هذا لـ getElementsByTagName وغيرها دون إضافة div إلى DOM:

var htmlText= '<html><head><title>Test</title></head><body><div id="test_ele1">this is test_ele1 content</div><div id="test_ele2">this is test_ele content2</div></body></html>';

var d = document.createElement('div');
d.innerHTML = htmlText;

console.log(d.getElementsByTagName('div'));

إذا كنت متزوجًا حقًا من فكرة documentFragment ، فيمكنك استخدام هذا الرمز ، ولكن ستظل بحاجة إلى لفه في div للحصول على وظائف DOM التي تتبعها:

function makeDocumentFragment(htmlText) {
    var range = document.createRange();
    var frag = range.createContextualFragment(htmlText);
    var d = document.createElement('div');
    d.appendChild(frag);
    return d;
}

https://src-bin.com

هذا شيء واجهتني بعض الصعوبة. لدي برنامج نصي من جانب العميل المحلي يحتاج إلى السماح للمستخدم بجلب صفحة ويب بعيدة والبحث عن تلك الصفحة الناتجة للنماذج. للقيام بذلك (بدون تعبير منطقي) ، أحتاج إلى تحليل المستند في كائن DOM قابل للعبور بالكامل.

بعض القيود التي أود التأكيد عليها:

  • لا أريد استخدام المكتبات (مثل jQuery). هناك الكثير من سخام على ما أحتاج القيام به هنا.
  • تحت أي ظرف من الظروف يجب تنفيذ البرامج النصية من الصفحة عن بعد (لأسباب أمنية).
  • يجب أن تكون واجهات برمجة تطبيقات DOM ، مثل getElementsByTagName متاحة.
  • تحتاج فقط إلى العمل في Internet Explorer ، ولكن في 7 على الأقل.
  • دعونا نتظاهر بأنه ليس لدي إمكانية الوصول إلى خادم. أفعل ، لكن لا يمكنني استخدامه لهذا.

ما حاولت

بافتراض أن لدي سلسلة مستندات HTML كاملة (بما في ذلك تعريف DOCTYPE) في html المتغير ، فإليك ما جربته حتى الآن:

var frag = document.createDocumentFragment(),
div  = frag.appendChild(document.createElement("div"));

div.outerHTML = html;
//-> results in an empty fragment

div.insertAdjacentHTML("afterEnd", html);
//-> HTML is not added to the fragment

div.innerHTML = html;
//-> Error (expected, but I tried it anyway)

var doc = new ActiveXObject("htmlfile");
doc.write(html);
doc.close();
//-> JavaScript executes

لقد حاولت أيضًا استخراج <head> و <body> العقد من HTML وإضافتها إلى عنصر <HTML> داخل الجزء ، ولكن لا يوجد حظ.

هل لديها أي أفكار؟


Answer #1

بافتراض أن HTML هو XML صالح أيضًا ، يمكنك استخدام loadXML()


Answer #2

لا يدعم DocumentFragment getElementsByTagName - وهو معتمد فقط بواسطة Document .

قد تحتاج إلى استخدام مكتبة مثل jsdom ، والتي توفر تطبيق DOM ويمكنك من خلاله البحث باستخدام getElementsByTagName وواجهات برمجة تطبيقات DOM أخرى. ويمكنك تعيينه لعدم تنفيذ البرامج النصية. نعم ، إنه "ثقيل" ولا أعرف إن كان يعمل في IE 7.


Answer #3

لاستخدام قدرات HTML DOM الكاملة دون تشغيل الطلبات ، دون الاضطرار للتعامل مع حالات عدم التوافق:

var doc = document.cloneNode();
if (!doc.documentElement) {
    doc.appendChild(doc.createElement('html'));
    doc.documentElement.appendChild(doc.createElement('head'));
    doc.documentElement.appendChild(doc.createElement('body'));
}

كل مجموعة! doc هو مستند html ، ولكنه ليس متصلاً بالإنترنت.


Answer #4

كمان : http://jsfiddle.net/JFSKe/6/

DocumentFragment لا يطبق أساليب DOM. يؤدي استخدام document.createElement بالتزامن مع innerHTML إزالة علامات <head> و <body> (حتى عندما يكون العنصر الذي تم إنشاؤه عنصرًا رئيسيًا ، <html> ). لذلك ، يجب البحث عن الحل في مكان آخر. لقد قمت بإنشاء دالة سلسلة-إلى-DOM عبر المستعرض ، والتي تستخدم إطار مضمّن غير مرئي.

سيتم تعطيل جميع الموارد والبرامج النصية الخارجية. انظر شرح الكود لمزيد من المعلومات.

الشفرة

/*
 @param String html    The string with HTML which has be converted to a DOM object
 @param func callback  (optional) Callback(HTMLDocument doc, function destroy)
 @returns              undefined if callback exists, else: Object
                        HTMLDocument doc  DOM fetched from Parameter:html
                        function destroy  Removes HTMLDocument doc.         */
function string2dom(html, callback){
    /* Sanitise the string */
    html = sanitiseHTML(html); /*Defined at the bottom of the answer*/

    /* Create an IFrame */
    var iframe = document.createElement("iframe");
    iframe.style.display = "none";
    document.body.appendChild(iframe);

    var doc = iframe.contentDocument || iframe.contentWindow.document;
    doc.open();
    doc.write(html);
    doc.close();

    function destroy(){
        iframe.parentNode.removeChild(iframe);
    }
    if(callback) callback(doc, destroy);
    else return {"doc": doc, "destroy": destroy};
}

/* @name sanitiseHTML
   @param String html  A string representing HTML code
   @return String      A new string, fully stripped of external resources.
                       All "external" attributes (href, src) are prefixed by data- */

function sanitiseHTML(html){
    /* Adds a <!-\"'--> before every matched tag, so that unterminated quotes
        aren't preventing the browser from splitting a tag. Test case:
       '<input style="foo;b:url(0);><input onclick="<input type=button onclick="too() href=;>">' */
    var prefix = "<!--\"'-->";
    /*Attributes should not be prefixed by these characters. This list is not
     complete, but will be sufficient for this function.
      (see http://www.w3.org/TR/REC-xml/#NT-NameChar) */
    var att = "[^-a-z0-9:._]";
    var tag = "<[a-z]";
    var any = "(?:[^<>\"']*(?:\"[^\"]*\"|'[^']*'))*?[^<>]*";
    var etag = "(?:>|(?=<))";

    /*
      @name ae
      @description          Converts a given string in a sequence of the
                             original input and the HTML entity
      @param String string  String to convert
      */
    var entityEnd = "(?:;|(?!\\d))";
    var ents = {" ":"(?:\\s|&nbsp;?|&#0*32"+entityEnd+"|&#x0*20"+entityEnd+")",
                "(":"(?:\\(|&#0*40"+entityEnd+"|&#x0*28"+entityEnd+")",
                ")":"(?:\\)|&#0*41"+entityEnd+"|&#x0*29"+entityEnd+")",
                ".":"(?:\\.|&#0*46"+entityEnd+"|&#x0*2e"+entityEnd+")"};
                /*Placeholder to avoid tricky filter-circumventing methods*/
    var charMap = {};
    var s = ents[" "]+"*"; /* Short-hand space */
    /* Important: Must be pre- and postfixed by < and >. RE matches a whole tag! */
    function ae(string){
        var all_chars_lowercase = string.toLowerCase();
        if(ents[string]) return ents[string];
        var all_chars_uppercase = string.toUpperCase();
        var RE_res = "";
        for(var i=0; i<string.length; i++){
            var char_lowercase = all_chars_lowercase.charAt(i);
            if(charMap[char_lowercase]){
                RE_res += charMap[char_lowercase];
                continue;
            }
            var char_uppercase = all_chars_uppercase.charAt(i);
            var RE_sub = [char_lowercase];
            RE_sub.push("&#0*" + char_lowercase.charCodeAt(0) + entityEnd);
            RE_sub.push("&#x0*" + char_lowercase.charCodeAt(0).toString(16) + entityEnd);
            if(char_lowercase != char_uppercase){
                RE_sub.push("&#0*" + char_uppercase.charCodeAt(0) + entityEnd);   
                RE_sub.push("&#x0*" + char_uppercase.charCodeAt(0).toString(16) + entityEnd);
            }
            RE_sub = "(?:" + RE_sub.join("|") + ")";
            RE_res += (charMap[char_lowercase] = RE_sub);
        }
        return(ents[string] = RE_res);
    }
    /*
      @name by
      @description  second argument for the replace function.
      */
    function by(match, group1, group2){
        /* Adds a data-prefix before every external pointer */
        return group1 + "data-" + group2 
    }
    /*
      @name cr
      @description            Selects a HTML element and performs a
                                  search-and-replace on attributes
      @param String selector  HTML substring to match
      @param String attribute RegExp-escaped; HTML element attribute to match
      @param String marker    Optional RegExp-escaped; marks the prefix
      @param String delimiter Optional RegExp escaped; non-quote delimiters
      @param String end       Optional RegExp-escaped; forces the match to
                                  end before an occurence of <end> when 
                                  quotes are missing
     */
    function cr(selector, attribute, marker, delimiter, end){
        if(typeof selector == "string") selector = new RegExp(selector, "gi");
        marker = typeof marker == "string" ? marker : "\\s*=";
        delimiter = typeof delimiter == "string" ? delimiter : "";
        end = typeof end == "string" ? end : "";
        var is_end = end && "?";
        var re1 = new RegExp("("+att+")("+attribute+marker+"(?:\\s*\"[^\""+delimiter+"]*\"|\\s*'[^'"+delimiter+"]*'|[^\\s"+delimiter+"]+"+is_end+")"+end+")", "gi");
        html = html.replace(selector, function(match){
            return prefix + match.replace(re1, by);
        });
    }
    /* 
      @name cri
      @description            Selects an attribute of a HTML element, and
                               performs a search-and-replace on certain values
      @param String selector  HTML element to match
      @param String attribute RegExp-escaped; HTML element attribute to match
      @param String front     RegExp-escaped; attribute value, prefix to match
      @param String flags     Optional RegExp flags, default "gi"
      @param String delimiter Optional RegExp-escaped; non-quote delimiters
      @param String end       Optional RegExp-escaped; forces the match to
                                  end before an occurence of <end> when 
                                  quotes are missing
     */
    function cri(selector, attribute, front, flags, delimiter, end){
        if(typeof selector == "string") selector = new RegExp(selector, "gi");
        flags = typeof flags == "string" ? flags : "gi";
         var re1 = new RegExp("("+att+attribute+"\\s*=)((?:\\s*\"[^\"]*\"|\\s*'[^']*'|[^\\s>]+))", "gi");

        end = typeof end == "string" ? end + ")" : ")";
        var at1 = new RegExp('(")('+front+'[^"]+")', flags);
        var at2 = new RegExp("(')("+front+"[^']+')", flags);
        var at3 = new RegExp("()("+front+'(?:"[^"]+"|\'[^\']+\'|(?:(?!'+delimiter+').)+)'+end, flags);

        var handleAttr = function(match, g1, g2){
            if(g2.charAt(0) == '"') return g1+g2.replace(at1, by);
            if(g2.charAt(0) == "'") return g1+g2.replace(at2, by);
            return g1+g2.replace(at3, by);
        };
        html = html.replace(selector, function(match){
             return prefix + match.replace(re1, handleAttr);
        });
    }

    /* <meta http-equiv=refresh content="  ; url= " > */
    html = html.replace(new RegExp("<meta"+any+att+"http-equiv\\s*=\\s*(?:\""+ae("refresh")+"\""+any+etag+"|'"+ae("refresh")+"'"+any+etag+"|"+ae("refresh")+"(?:"+ae(" ")+any+etag+"|"+etag+"))", "gi"), "<!-- meta http-equiv=refresh stripped-->");

    /* Stripping all scripts */
    html = html.replace(new RegExp("<script"+any+">\\s*//\\s*<\\[CDATA\\[[\\S\\s]*?]]>\\s*</script[^>]*>", "gi"), "<!--CDATA script-->");
    html = html.replace(/<script[\S\s]+?<\/script\s*>/gi, "<!--Non-CDATA script-->");
    cr(tag+any+att+"on[-a-z0-9:_.]+="+any+etag, "on[-a-z0-9:_.]+"); /* Event listeners */

    cr(tag+any+att+"href\\s*="+any+etag, "href"); /* Linked elements */
    cr(tag+any+att+"src\\s*="+any+etag, "src"); /* Embedded elements */

    cr("<object"+any+att+"data\\s*="+any+etag, "data"); /* <object data= > */
    cr("<applet"+any+att+"codebase\\s*="+any+etag, "codebase"); /* <applet codebase= > */

    /* <param name=movie value= >*/
    cr("<param"+any+att+"name\\s*=\\s*(?:\""+ae("movie")+"\""+any+etag+"|'"+ae("movie")+"'"+any+etag+"|"+ae("movie")+"(?:"+ae(" ")+any+etag+"|"+etag+"))", "value");

    /* <style> and < style=  > url()*/
    cr(/<style[^>]*>(?:[^"']*(?:"[^"]*"|'[^']*'))*?[^'"]*(?:<\/style|$)/gi, "url", "\\s*\\(\\s*", "", "\\s*\\)");
    cri(tag+any+att+"style\\s*="+any+etag, "style", ae("url")+s+ae("(")+s, 0, s+ae(")"), ae(")"));

    /* IE7- CSS expression() */
    cr(/<style[^>]*>(?:[^"']*(?:"[^"]*"|'[^']*'))*?[^'"]*(?:<\/style|$)/gi, "expression", "\\s*\\(\\s*", "", "\\s*\\)");
    cri(tag+any+att+"style\\s*="+any+etag, "style", ae("expression")+s+ae("(")+s, 0, s+ae(")"), ae(")"));
    return html.replace(new RegExp("(?:"+prefix+")+", "g"), prefix);
}

شرح الكود

sanitiseHTML وظيفة sanitiseHTML وظيفة sanitiseHTML الخاصة بي (انظر هذه الإجابة ). على الرغم من إعادة كتابة وظيفة sanitiseHTML بالكامل ، من أجل تحقيق أقصى قدر من الكفاءة والموثوقية.

بالإضافة إلى ذلك ، يتم إضافة مجموعة جديدة من RegExps لإزالة كافة البرامج النصية ومعالجات الأحداث (بما في ذلك expression() CSS expression() و IE7-). للتأكد من تحليل جميع العلامات بالشكل المتوقع ، يتم بدء البادئات المعدلة بـ <!--'"--> هذه البادئة ضرورية لتوزيع معالجات الأحداث المتداخلة بشكل صحيح بالتزامن مع علامات اقتباس غير مدمجة: <a id="><input onclick="<div onmousemove=evil()>"> .

يتم إنشاء هذه RegExps بشكل حيوي باستخدام الدالة cr / cri الداخلية ( C reate R eplace [ I nline]). تقبل هذه الوظائف قائمة بالمتحولات ، وتقوم بإنشاء واستبدال RE متقدم. للتأكد من أن كيانات HTML لا تقوم بفصل RegExp (يمكن كتابة refresh في <meta http-equiv=refresh> بطرق مختلفة) ، يتم إنشاء RegExps الذي تم إنشاؤه بشكل ديناميكي جزئياً بواسطة الدالة ae ( A ny E ntity).
تتم البدائل الفعلية عن طريق وظيفة (استبدال من قبل ). في هذا التنفيذ ، by إضافة data- قبل كل السمات المتطابقة.

  1. كل <script>//<[CDATA[ .. //]]></script> يتم تخطيط الأحداث. هذه الخطوة ضرورية ، لأن مقاطع CDATA تسمح </script> بالسلاسل داخل التعليمة البرمجية. بعد تنفيذ هذا الاستبدال ، من الآمن الذهاب إلى الاستبدال التالي:
  2. تتم إزالة علامات <script>...</script> المتبقية.
  3. تتم إزالة العلامة <meta http-equiv=refresh .. >
  4. جميع مسببي الأحداث والمؤشرات / السمات الخارجية ( href ، src ، url() ) مسبوقة data- ، كما هو موضح سابقًا.

  5. يتم إنشاء كائن IFrame . IFrames أقل احتمالاً لتسرب الذاكرة (على عكس htmlfile ActiveXObject). يصبح IFrame غير مرئي ، ويتم إلحاقه بالمستند ، بحيث يمكن الوصول إلى DOM. يتم استخدام document.write() لكتابة HTML إلى IFrame. document.open() و document.close() لتفريغ المحتويات السابقة للمستند ، بحيث يكون المستند الذي تم إنشاؤه نسخة مطابقة لسلسلة html المحددة.

  6. إذا تم تحديد وظيفة رد اتصال ، فسيتم استدعاء الدالة مع وسيطتين. الوسيطة الأولى هي مرجع إلى كائن document إنشاؤه. الوسيطة الثانية هي وظيفة ، والتي تدمر شجرة DOM التي تم إنشاؤها عند استدعاء. يجب استدعاء هذه الوظيفة عندما لا تحتاج إلى الشجرة بعد الآن.
    إذا لم يتم تحديد وظيفة رد الاتصال ، تقوم الدالة بإرجاع كائن يتكون من خاصيتين ( doc destroy ) ، والتي تتصرف بنفس الوسائط السابقة.

ملاحظات إضافية

  • designMode تعيين خاصية designMode إلى "تشغيل" إلى إيقاف إطار من تنفيذ النصوص البرمجية (غير معتمد في Chrome). إذا كان عليك الحفاظ على علامات <script> لسبب معين ، فيمكنك استخدام iframe.designMode = "On" بدلاً من ميزة تجريد النص البرمجي.
  • لم أتمكن من العثور على مصدر موثوق لـ htmlfile activeXObject . وفقًا لهذا المصدر ، يكون htmlfile أبطأ من IFrames وأكثر عرضة htmlfile في الذاكرة.

  • جميع السمات المتأثرة ( href ، src ، ...) مسبوقة data- . ويرد مثال على الحصول على / تغيير هذه السمات ل data-href :
    elem.getAttribute("data-href") و elem.setAttribute("data-href", "...")
    elem.dataset.href و elem.dataset.href = "..." .
  • تم تعطيل الموارد الخارجية. نتيجة لذلك ، قد تبدو الصفحة مختلفة تمامًا:
    <link rel="stylesheet" href="main.css" /> لا توجد أنماط خارجية
    <script>document.body.bgColor="red";</script> لا توجد أنماط مكتوبة
    <img src="128x128.png" /> لا توجد صور: قد يكون حجم العنصر مختلفًا تمامًا.

أمثلة

sanitiseHTML(html)
الصق هذا التطبيق المختصر في شريط الموقع. سيوفر خيارًا لحقن نص ، يعرض سلسلة HTML المعقم.

javascript:void(function(){var s=document.createElement("script");s.src="http://rob.lekensteyn.nl/html-sanitizer.js";document.body.appendChild(s)})();

أمثلة الكود - string2dom(html) :

string2dom("<html><head><title>Test</title></head></html>", function(doc, destroy){
    alert(doc.title); /* Alert: "Test" */
    destroy();
});

var test = string2dom("<div id='secret'></div>");
alert(test.doc.getElementById("secret").tagName); /* Alert: "DIV" */
test.destroy();

المراجع البارزة





dom