ホームページ > ウェブフロントエンド > jsチュートリアル > innerHTML_javascript スキルに挿入されたスクリプトを実行するコード

innerHTML_javascript スキルに挿入されたスクリプトを実行するコード

PHP中文网
リリース: 2016-05-16 19:07:53
オリジナル
970 人が閲覧しました

ajax プログラミングを行う場合、xmlhttp で取得したページのコンテンツを innerHTML を介してコンテナ (p、span、td など) に割り当てる必要があることがよくあります。 innerHTML に代入 ページコンテンツに外部スクリプト、内部スクリプトを問わずスクリプトが含まれている場合、(1)は実行されない場合があります。場合によっては、この問題は些細で無視できることもありますが、場合によってはこの問題が非常に深刻であり、プログラムが期待した結果を得られない可能性があります。したがって、この問題を解決する必要があります。

MSDN を読むと、innerHTML に挿入されたすべてのスクリプトが実行できないわけではないことがわかります。このスクリプトの script タグに defer 属性が含まれている場合、IE はこれらのスクリプトを正しく実行します。しかし残念ながら、Moziila/Firefox と Opera ではこれが行われません。script タグに defer 属性が設定されているかどうかに関係なく、これらのブラウザは IE のように innerHTML に挿入されたスクリプトを実行しません。

しかし、スクリプトが実行されるかどうかに関係なく、確実に言えることは、これらのスクリプトが実際に innerHTML に挿入されているということです。信じられない場合は、警告して確認してください。しかし、実際に注意してみると、例外があることもわかります。つまり、スクリプトが innerHTML コンテンツの先頭にある場合、IE ブラウザはこのスクリプトを無視しますが、Moziila/Firefox および Opera は無視しません。

さて、問題分析はほぼ完了しました。解決方法を見てみましょう。

解決策は実際には非常に簡単です。つまり、innerHTML に挿入されたすべてのスクリプトを取り出し、それらを 1 つずつ実行します。しかし、最初に上記の 2 つの問題を解決する必要があります。

まず最初の質問、IE の innerHTML で defer 属性を使用したスクリプトの繰り返し実行を回避する方法を見てみましょう。これは簡単で、ブラウザが IE かどうかを判断し、次に実行するスクリプトに defer 属性があるかどうかを確認するだけです。 IE ブラウザを判断する際には、Opera のブラウザ認識に騙されないように注意する必要があります。以下のコードでこれがどのように行われるかを見ていきます。

次に、IE が innerHTML の先頭にあるスクリプトを無視する問題を見てみましょう。これも簡単に解決できます。 innerHTML に挿入したいコンテンツの先頭に、スクリプトではないコンテンツを追加するだけで完了です。ただし、空の内容、スペース、キャリッジ リターン、ライン フィードなどを含むタグを追加しないでください。追加しても機能せず、先頭のスクリプトは無視されます。を追加しないでください。追加すると、最初のスクリプトが無視されるのを防ぐことができますが、それでも元のコンテンツの表示に影響しますが、うるさいユーザーにとっては耐えられないかもしれません。したがって、追加コンテンツが悪影響を及ぼさずに開始スクリプトが無視されるのを防ぐために、次のコンテンツを追加します:

上記のコンテンツはある程度の長さがありますが、表示されません。また、挿入されたタグには ID や名前がないため、元のコンテンツの一部のタグとは異なります。衝突。ただし、ここで注意すべき点が 1 つあります。他のブラウザではこの表示がサポートされていない可能性があるため、IE であるかどうかを判断してから、このコンテンツを追加するかどうかを決定する必要があります。CSS の変更はありません (Opera Mini など)。これを追加 このコードは最終的な表示効果に影響します。

スクリプトを取り出して実行する方法を見てみましょう。

スクリプトの削除は簡単です。innerHTML が配置されているオブジェクトの getElementsByTagName メソッドを使用するだけです。このメソッドは、ほとんどすべてのコンテナ タグで機能します。スクリプトを取り出したら、それが外部スクリプトなのか内部スクリプトなのかを一つ一つ判断する必要があります。

まず外部スクリプトを見てみましょう。外部スクリプトの場合は、最初に外部スクリプトのコピー オブジェクトを作成し、その defer 属性を true に設定する方法を選択しました (これは、IE ブラウザが正しく実行できるようにするためです)。次に、appendChild メソッドを使用して、このコピー オブジェクトを head に挿入します。ここで、innerHTML が配置されているオブジェクトにそれを挿入すればよいのではないかと疑問に思うかもしれません。 innerHTMLがあるオブジェクトに挿入した方が良いのではないでしょうか?試してみるとわかりますが、innerHTML が配置されているオブジェクトに挿入すると、IE ブラウザでは問題ありませんが、Mozilla/Firefox および Opera ブラウザでは若干の問題が発生します。問題は、Firefox でこれを実行すると、ブラウザが応答を停止し (これは Firefox 1.5 でのテスト結果であり、他のバージョンでこの問題が発生するかどうかは不明です)、Opera ではスクリプトが不可解にも 2 回実行されることです (これは Opera 8.5 でのテスト結果です。Opera の他のバージョンでこの問題が発生するかどうかはまだ不明です。これらの問題を回避するために、ヘッドに挿入することにしました。

内部スクリプトを見てみましょう。内部スクリプトの内容は、スクリプト オブジェクトの text 属性を使用して直接取得できます。ここでは、innerHTML 属性の代わりに script オブジェクトの text 属性を使用します。 Opera ブラウザ、スクリプト オブジェクト innerHTML 属性は空であり、スクリプト コンテンツの取得に使用できるのは text 属性のみです。内部スクリプトを実行するには、eval を使用するだけです。ただし、HTML のコメントタグ内にスクリプトが含まれている場合があるため、コメントタグを削除しないと IE でエラーが発生します。

上記の分析は完璧に見えますが、実際にはまだ問題があります。1 つは document.write と document.writeln の問題です。この問題は Blueidea にあり、それを置き換えるというアイデアが得られます。デフォルトの document.write メソッドと document.writeln メソッドは文字列置換を使用するため、内部スクリプトに対してのみ有効であり、外部スクリプトに対しては無効です。そこで、document write を直接置き換える、より一般的な方法を考えました。 document.write と document.writeln が再定義されるため、内部スクリプトが実行されるか外部スクリプトが実行されるかに関係なく、document.write と document.writeln を自分たちで定義します。ただし、副作用もあります。つまり、これら 2 つの関数は以前のように現在のページで使用できなくなります。ただし、これら 2 つの関数は通常、ページが読み込まれた後は使用されなくなるため、次のような副作用が発生します。再定義しても影響は最小限です。しかし、もう 1 つの問題は、これにもかかわらず、 document.write または document.writeln によって出力されたコンテンツが、コンテンツを配置するコンテナーに単に追加されるだけであることを保証できないことです。

もう 1 つは eval によって引き起こされる問題です。1 つは Blueidea で Hutia が指摘したスコープの問題です。もう 1 つは、eval を使用して内部スクリプトを実行すると、外部スクリプトが読み込まれる前に内部スクリプトが読み込まれることです。ロードされました。実行が開始されました。これら 2 つの問題を解決するには、window.setTimeout 関数を使用して、各スクリプトを実行する前に一定期間遅延させることができます。外部スクリプトの遅延時間を長く設定して完全にロードできるようにする一方で、内部スクリプトの場合は遅延時間を長く設定できます。スクリプトの実行時間は通常非常に短いため、設定できる値は非常に短くなります。これにより、スコープが変更されないことが保証されるだけでなく、スクリプトの実行順序が変更されないことも基本的に保証されます (このメソッドはそうではありません)。実行順序を 100% 効率的に確保するのに優れています。ネットワークが非常に混雑している場合は、設定された時間内に外部スクリプトがロードされない可能性がありますが、少なくとも eval を直接使用するよりははるかに優れています。

——————————–
(1) 注: 以下ではスクリプトが実行される状況があるため、ここでは修飾子「may」を使用します。これが表示されます。


前述の方法に従って実装すると、ほとんどのスクリプトは正常に実行できます。ただし、スクリプトに defer 属性がある場合、IE はそのコードを単独で実行するため (前述)、実行順序が乱れます。また、document.writeやdocument.writelnで書いたコードはスクリプトのある場所ではなく最後に追加されるので、これも問題です。

これら 2 つの問題を解決するには、以前の解決策にいくつかの変更を加える必要があります。まず、コンテンツを innerHTML に割り当ててから、そこからスクリプトを取得することはできません。スクリプトを取得するには、コンテンツを直接分析する必要があります。また、スクリプト以外のHTML部分をinnerHTMLに直接代入することはできず、スクリプト実行後に元のHTMLの内容とdocument.writewritelnで記述した内容を順番にマージしてからinnerHTMLに代入する必要があります。ここで、タグのコンテンツの半分が存在する可能性があるため、このコンテンツの一部を innerHTML に部分的に接続することはできません。その場合、ブラウザーでエラーが発生しやすくなります。そして、ページが繰り返し更新されることがわかります。最初にバッファに入れて、最後に innerHTML に代入すると、この問題は発生しません。

さらに、スクリプトをバッファーに入れる利点は、スクリプトの実行後に、バッファーに新しいスクリプトがあるかどうかを確認し、存在する場合はそれを再帰的に実行できることです。 .write と document で記述したスクリプトも実行できます。

2006-6-4 更新:

innerHTML に挿入されたスクリプトが innerHTML に挿入されたオブジェクトを取得できない問題を修正しました。 (思い出させてくれたネチズン DE に感謝します)。

同じコンテナ内のコンテンツに設定される共有ロックを追加しました。これにより、同じコンテナ内のコンテンツを継続的に設定するときに競合が発生しなくなります。 (シンガポールのネチズン、Jason Li に思い出させてくれてありがとう)。

2006-5-29 更新:

同じ外部スクリプトの 2 回目の読み込み速度を向上させるために、外部スクリプト キャッシュ機能の使用を追加しました。

2006-5-23 更新:

熱心なユーザー johnZEN によって思い出されたように、複数のコンテナの内容を同時に設定するときに競合が発生しないように、共有ロックが追加されました。 。

netizen udbjatwfn が指摘したように、IE の内部スクリプト実行スコープ エラーが修正されました。

以下は私の最終的な実装コードです:

ダウンロード: innerhtml.js

コードをコピー コードは次のとおりです:


/* innerhtml.js
* Copyright Ma Bingyao  
* バージョン: 1.9
* 最終更新日: 2006-06-04
* このライブラリは無料です。  再配布したり、変更したりすることができます。 
* http://www.php.cn/
*/

var global_html_pool = []; 
var global_script_pool = []; 
var global_script_src_pool = []; 
var global_lock_pool = []; 
var innerhtml_lock = null; 
var document_buffer = ""; 

function set_innerHTML(obj_id, html, time) {
if (innerhtml_lock == null) {
innerhtml_lock = obj_id; 
}
else if (typeof(time) == "未定義") {
global_lock_pool[obj_id "_html"] = html; 
window.setTimeout("set_innerHTML('" obj_id "', global_lock_pool['" obj_id "_html']);", 10); 
戻る; 
}
else if (innerhtml_lock != obj_id) {
global_lock_pool[obj_id "_html"] = html; 
window.setTimeout("set_innerHTML('" obj_id "', global_lock_pool['" obj_id "_html'], " time ");", 10); 
戻る; 
}

function get_script_id() {
return "script_" (new Date()).getTime().toString(36)
Math.floor(Math.random() * 100000000).toString(36); 
}

document_buffer = ""; 

document.write = function (str) {
document_buffer = str; 
}
document.writeln = function (str) {
document_buffer = str "n"; 
}

global_html_pool = []; 

var scripts = []; 
html = html.split(//i); 
for (var i = 0; i < html.length; i ) {
global_html_pool[i] = html[i].replace(/ scripts[i] = {text: '', src: '' }; 
scripts[i].text = html[i].substr(global_html_pool[i].length); 
scripts[i].src = scripts[i].text.substr(0, scripts[i].text.indexOf('>') 1); 
scripts[i].src = scripts[i].src.match(/srcs*=s*("([^"]*)"|'([^']*)'|([^s ]*)[s>])/i);
if (scripts[i].src) {
if (scripts[i].src[2]) {
scripts[i].src = scripts[i].src[2];
}
else if (scripts[i].src[3]) {
                スクリプト[i].src = スクリプト[i].src[3]; 
}
else if (scripts[i].src[4]) {
scripts[i].src = scripts[i].src[4]; 
}
else {
scripts[i].src = ""; 
}
scripts[i].text = ""; 
}
else {
scripts[i].src = ""; 
scripts[i].text = scripts[i].text.substr(scripts[i].text.indexOf('>') 1); 
scripts[i].text = scripts[i].text.replace(/^s*