2

つまり、一部のスクリプトが親要素の InnerHtml プロパティを介して要素を挿入したためにページ上の特定の要素がページ上にあるかどうか、またはそれらがダウンロードした元の HTML ドキュメントの一部であったかどうかを知る必要があります。これら 2 つの可能性は、この (ばかげた) アプリケーションでは非常に異なることを意味します。

実際のユースケース:

サード パーティのスクリプトは、要素の InnerHtml 属性を設定することにより、ページ上のランダムなノード要素を更新します。私はブラウザ (WPF / GeckoFx / XulRunner) を完全に制御でき、(新しい) JS を自由に挿入および変更できますが、高度に難読化されたサードパーティのスクリプトを変更する洞察や機能はまったくありません。

必要なデータを取得する唯一の方法は、ページの読み込み後に、画面上の特定の要素が存在する場合は、サードパーティのスクリプト (innerHtml) によって読み込まれたかどうか、または以前に元の Html ドキュメントの一部であったかどうかを判断することです。サードパーティのスクリプトが実行されました。


元のページには多くのインライン スクリプトが含まれているため、元のページの HTML コンテンツ ソースと最終的な状態を単純に比較することは困難です。

誰にもアイデアはありますか?

4

3 に答える 3

1

残念ながら、ミューテーション オブザーバーを使用するという提案は、この状況には当てはまりません。Mutation オブザーバーは、dom ノードがページに追加された理由にとらわれず、追加されたことのみを報告します。つまり、DOM の一部が追加されたのは、ページがまだ読み込まれているためか、スクリプトが起動してコンテンツを動的に追加したためかを判断することは不可能です。

でも

この記事では、dom 内のすべての要素の InnerHTML getter/setter プロパティを上書きする方法について説明します: http://msdn.microsoft.com/en-us/library/dd229916(v=vs.85).aspx InnerHTML 以来常にjavascriptによって呼び出されるため、この関数呼び出しを使用してDOMの特定の部分がロードされたかどうかを知るのは簡単です。

これはほぼ確実にやり過ぎであり、ほとんどのアプリケーションにとっては良い考えではありませんが、このような奇妙な状況や js フレームワークの構築では、おそらく理にかなっています。

その記事がある時点でオフラインになった場合、私の最初のコードは次のようになります。

var elem = isInIE() ? HTMLElement : Element;    // IE and FF have different inheritance models, behind the scenes.
var proxiedInnerHTML = Object.getOwnPropertyDescriptor(elem.prototype, "innerHTML");

Object.defineProperty(elem.prototype, "innerHTML", {
    set: function ( htmlContent )
    {
        // custom code goes here

        proxiedInnerHTML.set.call(this, htmlContent);
    }); 

古いブラウザーでは警告する必要があります。または、間違った要素 (HTMLElement と Element) を使用すると、プロパティ定義ではなく、innerHTML 呼び出しで呼び出しが失敗します。

ブラウザでのプロトタイプの扱い:

このブロックは FF と IE でテストしましたが、Chrome ではテストしませんでした。さらに重要なことは、w3c 仕様にはブラウザーが要素型の継承を処理する方法を指定する保証がないことを示す投稿を見つけたことです。そのため、HtmlDivElement が将来または過去に InnerHTML の HtmlElement または Element 基本メソッドを呼び出すという保証はありません。特定のブラウザのバージョン。

とはいえ、すべて予約済みの html キーワードを使用して Web ページを作成し、この手法がそれらで機能するかどうかをテストするのは非常に簡単です。IE と FF の場合、2015 年 1 月の時点で、この手法は全面的に機能します。

古いブラウザのサポート:

私は使用していませんが、古いブラウザでは使用できます

document.__defineGetter__("test", /* getter function */ );
document.__defineSetter__("test", /* setter function */ );
document.__lookupGetter__("test");
document.__lookupSetter__("test");

この道を歩ませてくれたRobGに感謝します

于 2015-01-12T17:05:22.383 に答える
1

スクリプトが jQuery に依存している場合は非常に簡単です$.holdReady()。オブザーバーがリッスンするまで、準備完了イベントの発生を遅らせるだけです。

HTML:

<h1>Sample title</h1>
<p>Sample paragraph</p>

J:

$(function() {
    $('body').append("<p>Foo</p>").append("<p>Bar</p>");
});

(function() {
    $.holdReady(true);
    var observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            console.log(mutation.type);
        });
    });
    var target = document.querySelector('html');
    var config = {
        childList: true,
        attributes: true,
        subtree: true,
        characterData: true
    };
    setTimeout(function() {
        observer.observe(target, config);
        $.holdReady(false);
    }, 1);
}());

上記のように、他のスクリプトが ready イベントにバインドする場所に関係なく、これは機能します。


ただし、言うまでもなく、他のスクリプトが jQuery に依存していると想定することは、常に当てにできるものではありません。それとは関係なく機能するソリューションを探している場合は、注意が必要です。

HTMLは以前のままです。ボディの最後に
Js :

$(function() {
    $('body').append("<p>Foo</p>").append("<p>Bar</p>");
});

(function() {
    var observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            console.log(mutation.type);
        });
    });
    var target = document.querySelector('html');
    var config = {
        childList: true,
        attributes: true,
        subtree: true,
        characterData: true
    };
    observer.observe(target, config);
}());

期待される機能を得るには、このスクリプト ブロックが本文の最後にある絶対的な最後のスクリプト ブロックであることを確認してください。これにより、すべての静的 DOM が既に存在し、正しい時間にリッスンを開始できることが保証されます。
load または ready イベントが発生した後に、他のすべてのスクリプトが DOM の変更を開始すると想定しています。そうでない場合は、それに応じてスクリプト ブロックを移動し、このスクリプトが DOM 解析の最後に起動し、他のスクリプトがこのスクリプトの後に起動するようにします。

これを完全にテストしたわけではありませんが、これで始められるはずです。

于 2015-01-09T00:25:08.353 に答える
1

ミューテーション オブザーバーは、(ほとんどの場合) 次の仮定に基づいて動作する必要があります。

  • HTML パーサーは、ツリーの一番下のブランチに沿ってノードを追加するだけです。つまり、それらはすべてツリー順に到着するはずです。そうでないものはすべてスクリプト生成です
  • Mutation Observer バッチ間で最後に挿入されたノードを追跡するのは簡単です
  • .innerHTML はノードを追加するだけでなく、現在の子ノード、特に頻繁に存在する空白テキスト ノードまたはコメントも削除します。html パーサー otoh は削除を生成しません。
  • dom ready イベント後のすべての変更は、明らかに JavaScript によって実行されます。
  • 最も近い一意に識別可能な祖先ノードの内容を、スクリプトを実行せずに html ソースから生成されたドキュメント オブジェクトと比較することにより、サブツリーを二重チェックできるかどうか疑わしい場合 (XMLHttpRequest は、テキストではなくドキュメント形式で内容を返すことができます)
  • サードパーティのスクリプトが読み込まれるまで、信頼できるスクリプトによって行われた変更を無視することもできます。これにより、少なくともいくつかの誤検知を回避できます。ただし、その時点以降では、どのスクリプトが変更を担当したかを明らかに区別することはできません。

そのため、突然変異イベントの分類器を構築して、スクリプトによって生成されたノードとパーサーによって生成されたノードを正確に区別できるようにすることが可能になるはずです。確信が持てないエッジケースとそれを改良する方法がいくつかありますが、詳細を知らなくても、これで十分だと思います.

ブラウザを完全に制御できるため、特権コードやフレーム スクリプトでDOMWindowCreatedイベントを介して独自のスクリプトをできるだけ早く実行できます。

于 2015-01-09T21:19:53.780 に答える