8

私が書いた関数を呼び出すJavaScript関数がありますが、そのコードを制御することはできません。私の関数はDOMを使用してiFrameを生成し、そのsrcを定義してから、別のDOM要素に追加します。ただし、関数が戻る前に、つまり含まれている関数の継続的な実行を許可する前に、iFrameが完全にロードされていることが不可欠です。

これが私が試したことと、なぜそれらが機能しないのかです:

1. SetTimeoutオプション:
99.999%の確率で、これが答えです。実際のところ、私がJavaScriptで指導してきた過去10年間、私は常にこのオプションを使用するためにコードをリファクタリングできると主張してきましたが、そうでないシナリオが存在するとは信じていませんでした。さて、ようやく見つけました!問題は、関数がインラインで呼び出されているため、iFrameの読み込みが完了する前に次の行が実行されると、スクリプトが完全に無効になり、スクリプトが完了した瞬間から外部スクリプトが続行されることです。ある種のコールバックは機能しません

2.「何もしない」ループ:
while(// iFrameがロードされていない){//何もしない}で使用するこのオプション。理論的には、フレームがロードされるまでこれは戻りません。問題は、これがすべてのリソースを占有するため、iFrameが読み込まれないことです。このトリックは、ひどく専門的ではない、汚いなど、インライン遅延が必要な場合に機能しますが、完了するには外部スレッドが必要なため、機能しません。
FFでは、数秒後にスクリプトが一時停止し、応答しないスクリプトがあることを示すアラートがポップアップ表示されます。そのアラートが発生している間、iFrameをロードしてから関数を返すことができますが、ブラウザーを10秒間フリーズさせてから、ユーザーにエラーを正しく閉じるように要求することはできません。

3.モデルダイアログ:
FFポップアップで関数の実行を停止しながらiFrameを読み込むことができたという事実に触発され、それについて考えると、モーダルダイアログが実行を停止し、他のスレッドを続行できるようにする方法であることがわかりました。 !!素晴らしいので、他のモーダルオプションを試すことにしました。alert()のようなものは美しく機能します!ポップアップすると、10分の1秒しか表示されなくても、iFrameは完了でき、すべてが正常に機能します。また、1/10秒では不十分な場合に備えて、ソリューション2のwhileループにモデルダイアログを配置できます。これにより、iFrameが時間内に読み込まれるようになります。甘いね?スクリプトを実行するために、ユーザーが却下するための非常に専門的でないダイアログをポップアップする必要があるという事実を除いて。私はこの行動のこの費用便益について自分自身と戦いました、しかし、その後、1つのページでコードが10回呼び出されるというシナリオに遭遇しました。ページにアクセスする前に10個のアラートを閉じる必要がありますか?!これは、90年代後半のスクリプトキディページを思い出させるものであり、オプションではありません。

4.他にも数え切れないほどの遅延スクリプトが
あります。約10個のjQuery遅延またはスリープ関数があり、それらのいくつかは実際には非常に巧妙に開発されていますが、どれも機能しませんでした。いくつかのプロトタイプオプション、そして繰り返しますが、私が見つけたものはどれもそれを行うことができませんでした!十数かそこらの他のライブラリとフレームワークは、私が必要なものを持っていると主張しましたが、残念ながら、それらはすべて私に誤った希望を与えるために共謀しました。

組み込みのモデルダイアログは実行を停止し、他のスレッドは続行できるため、ユーザー入力なしで同じことを行うためのコードアクセス可能な方法が必要であると確信しています。

コードは文字通り数千行から数千行であり、独自仕様であるため、この問題の小さな例を作成しました。変更できる唯一のコードはonlyThingYouCanChange関数にあることに注意することが重要です。

テストファイル:

<html>
<head>
</head>
</html>
<body>
<div id='iFrameHolder'></div>
<script type='text/javascript'>
function unChangeableFunction()
{
    new_iFrame = onlyThingYouCanChange(document.getElementById('iFrameHolder'));
    new_iFrame_doc = (new_iFrame.contentWindow || new_iFrame.contentDocument);
    if(new_iFrame_doc.document)new_iFrame_doc=new_iFrame_doc.document;
    new_iFrame_body = new_iFrame_doc.body;
    if(new_iFrame_body.innerHTML != 'Loaded?')
    {
        //The world explodes!!!
        alert('you just blew up the world!  Way to go!');
    }
    else
    {
        alert('wow, you did it!  Way to go!');
    }
}
var iFrameLoaded = false;
function onlyThingYouCanChange(objectToAppendIFrameTo)
{
    iFrameLoaded = false;
    iframe=document.createElement('iframe');
    iframe.onload = new Function('iFrameLoaded = true');
    iframe.src = 'blank_frame.html'; //Must use an HTML doc on the server because there is a very specific DOM structure that must be maintained.
    objectToAppendIFrameTo.appendChild(iframe);
    var it = 0;
    while(!iFrameLoaded) //I put the limit on here so you don't 
    {
        //If I was able to put some sort of delay here that paused the exicution of the script, but did not halt all other browser threads, and did not require user interaction we'd be golden!
        //alert('test'); //This would work if it did not require user interaction!
    }
    return iframe;
}
unChangeableFunction();
</script>
</body>

blank_frame.html:

<html>
<head>
</head>
<body style='margin:0px'>Loaded?</body>
</html>


これが私がレスポンダーからのアイデアを組み合わせることから作った答えです!あなたたち最高!
変更を許可された関数の新しいソース:

function onlyThingYouCanChange(objectToAppendIFrameTo)
{
    iFrameLoaded = false;
    iframe=document.createElement('iframe');
    iframe.onload = new Function('iFrameLoaded = true');
    iframe.src = 'blank_frame.html'; //Must use an HTML doc on the server because there is a very specific DOM structure that must be maintained.
    objectToAppendIFrameTo.appendChild(iframe);
    var it = 0;
    while(!iFrameLoaded) //I put the limit on here so you don't
    {
        if (window.XMLHttpRequest)
        {
            AJAX=new XMLHttpRequest();
        }
        else
        {
            AJAX=new ActiveXObject("Microsoft.XMLHTTP");
        }
        if (AJAX)
        {
            AJAX.open("GET", 'slow_page.php', false);
            AJAX.send(null);
        }
        else
        {
            alert('something is wrong with AJAX!');
        }

        //If I was able to put some sort of delay here that paused the exicution of the script, but did not halt all other browser threads, and did not require user interaction we'd be golden!
        //alert('test'); //This would work if it did not require user interaction!
    }
    return iframe;
}

slow_page.php:

<?
usleep(100000);//sleep for 1/10th of a second, to allow iFrame time to load without DOSing our own server!
?>

その関数の外に変更できるものは何もないと述べたことに注意したいと思います。phpページを追加すると、その「ルール」に違反しましたが、場合によってはそれを行うことができました。それができなかった場合は、 slow_page.phpの代わりにblank_frame.htmlを呼び出すことができ、同じ量の応答があったと仮定すると、1回だけ呼び出す必要がありました(つまり、フレームの読み込みごとに2回)。 iFrameの読み込み時間。何らかの理由でiFrameの読み込みが遅い場合は、2ceと呼ばれることがあります(サーバーへの合計3回の呼び出し)

4

8 に答える 8

3

ええ、JavaScript がシングル スレッドであるという事実は、ここであなたを本当に苦しめます。意図的に遅いページへの同期 ajax 呼び出しを使用してスリープをエミュレートすることはできますが、必要な結果は得られません。変更不可能な関数が呼び出される前に、IFrame が読み込まれていることを確認してみませんか?

于 2011-01-02T00:21:03.763 に答える
2

本当に必要なのは、iFrame コンテンツが読み込まれたときに発生するイベントです。iFrame 内のページには独自のイベントがあり、親ページのスクリプトにアクセスできるため、これは実際には非常に簡単です。ただし、iFrame の内容を変更できる必要があります。

iFrame では、このコードが必要になります

// 好きな DOMReady 関数を使用しないと、window.onload が機能します
window.addEventListener('DOMContentLoaded', function() {
    if (parent.window.myFunction) {
        親.window.myFunction();
    }
}、 間違い);

次に、親ページで「myFunction」という関数を作成し、起動する必要があるすべてのスクリプトをそこに配置します。これは毎回うまくいくはずです。

編集:これを機能させるには、実際には2つの関数が必要です。それは実際にはオプションではないと想定しているので、1 つの関数をハックして 2 つの関数を含め、必要なときに適切な部分を呼び出します。

関数 onlyThingYouCanChange(stringOrObject) {
    関数 createIFrame(objectToAppendIFrameTo) {
        // このコメントは、iFrame を追加するすべてのコードを表します
    }
    関数 onIFrameReady() {
        // このコメントは、iFrame の準備ができたときに実行したいすべてのことを表しています
    }

    // その骨
    if (stringOrObject === "iFrameLoaded") {
        onIFrameReady();
    } そうしないと {
        createIFrame(stringOrObject);
    }
}

iFrame のスクリプトは、次のように変更する必要があります。

// 好きな DOMReady 関数を使用しないと、window.onload が機能します
window.addEventListener('DOMContentLoaded', function() {
    if (parent.window.onlyThingYouCanChange) {
        parent.window.onlyThingYouCanChange('iFrameLoaded');
    }
}、 間違い);

私はそれをテストしていませんが、理論的にはそれができるはずです

于 2011-01-02T00:15:24.797 に答える
2

: これは非常にハックであり、実際の状況では使用しません。他の潜在的な問題の中でも、十分なトラフィックがあれば、自分自身で DDOS を実行する可能性があります。

非非同期 (A)JAX 呼び出しを行うことで、スリープ機能を作成できます。一部の古いブラウザーでは、これによりすべてがフリーズする可能性がありますが、少なくともユーザーの応答は必要ありません。

while (!iFrameLoaded)
{
    if (XMLHTTPRequest) {
        var request = new XMLHttpRequest();
    } else {
        var request = new ActiveXObject("Microsoft.XMLHTTP");
    }

    request.open('GET', 'anyoldfile.htm', false);
    request.send();

    // check if the iframe is loaded and set iFrameLoaded
}
于 2011-01-02T00:17:37.090 に答える
1

XPCOM を使用した途方もなく単純な ;-} 回答:

// Get instance of the XPCOM thread manager.
var threadManager=Components.classes['@mozilla.org/thread-manager;1'].getService(
                      Components.interfaces.nsIThreadManager);
// Release current thread.
function doThread() {threadManager.currentThread.processNextEvent(false);};

// Event enabled delay, time in ms.
function delay(time) {
  var end;
  var start=Date.now();
  do {
    end=Date.now();
    doThread();
  } while ((end-start) <= time);
}

最近のバージョンの Firefox で動作します。エクスプローラーには希望がありません。

于 2011-09-27T22:23:43.493 に答える
1

この場合、再帰関数が役立つ場合があります。グローバル変数がフレームがロードされたことを示すまで関数を呼び出すだけです

var iFrameStarted = false; //you need two global vars
var iFrameLoaded  = false;


function onlyThingYouCanChange(objectToAppendIFrameTo)
{
  if (iFrameLoaded=false)  // if the frame has loaded then you are done. skip everything and return iframe
  { if (iFrameStarted = false) //otherwise start the frame if it has not been
  {
   iFrameStarted = true;
   iframe=document.createElement('iframe');
   iframe.onload = new Function('iFrameLoaded = true');
   iframe.src = 'blank_frame.html'; //Must use an HTML doc on the server because there is a very specific DOM structure
   objectToAppendIFrameTo.appendChild(iframe);
   var it = 0;
   for (i=0;i<10000;i++) {}  //slow down execution so you are not recursing yourself to death
   onlyThingYouCanChange(objectToAppendIFrameTo);   //start the recursion process

  }
  else  //the frame has been started so continue recursion until the frame loaded
  {
   for (i=0;i<10000;i++) {}  //slow down execution so you are not recursing yourself to death
   onlyThingYouCanChange(objectToAppendIFrameTo);   recursively call your function until the frame is loaded

  }

}

return iframe;  //you only get here when all the recursions are finished   
}
于 2012-07-01T08:03:28.363 に答える
0

元のコードをどれだけ単純化したかによっては、適用できない別の解決策があります。onload ハンドラーを設定し、エラーをスローしてから、unChangeableFunctiononload ハンドラーを呼び出すことができます。

function onlyThingYouCanChange(objectToAppendIFrameTo)
{
    // using global variable func_called
    if (!func_called) {
        func_called = true;
        var iframe=document.createElement('iframe');
        iframe.src = 'blank_frame.html';
        iframe.id = 'myIframe';
        iframe.onload = function() {
            unChangeableFunction();
        };
        objectToAppendIFrameTo.appendChild(iframe);

        throw new Error('not an error');
    } else {
        return document.getElementById('myIframe');
    }
}

この関数 ( などunChangeableFunction) は 2 回呼び出されます。最初のインスタンスで 1 回、次にonloadハンドラーがトリガーされたときにもう一度呼び出されます。2 つの異なる経路はこれを反映しています。

繰り返しますが、これはハッキーであり、JS のエラー機能の明確な乱用です。

于 2011-01-02T00:39:32.837 に答える
0

次のように cookie と setTimeout を使用できます。

blank_frame.html にスクリプトを追加します。

<script type="text/javascript">
function deleteCookie(cookie_name)
{
  var cookie_date=new Date();
  cookie_date.setTime(cookie_date.getTime()-1);
  document.cookie=cookie_name+="=;expires="+cookie_date.toGMTString();
}
function setCookie(name,value,expires,path,domain,secure){
    document.cookie=name+"="+escape(value)+((expires)?"; expires="+expires.toGMTString():"")+((path)?"; path="+path:"")+((domain)?"; domain="+domain:"")+((secure)?"; secure":"");
}

window.onload=function(){
    setCookie('iframe_loaded','yes',false,'/',false,false);
}
</script>

iframe_loaded基本的に、 valueを持つ Cookie を追加していますyes。IMO ページをリロードする場合は同じことを行う必要があるため、Cookie を削除することをお勧めします。setCookie 関数呼び出しでドメインを設定することもできます。

メインファイルでは、Cookie が存在するかどうかを確認する関数で setTimeout を使用します。存在する場合、関数はコードのように iframe を返します。

function onlyThingYouCanChange(objectToAppendIFrameTo)
{
    function get_cookie(cookie_name){
        var results = document.cookie.match('(^|;) ?'+cookie_name+'=([^;]*)(;|$)');
        return results?unescape(results[2]):null;
    }
    function deleteCookie(cookie_name){
        var cookie_date=new Date();
        cookie_date.setTime(cookie_date.getTime()-1);
        document.cookie=cookie_name+="=;expires="+cookie_date.toGMTString();
    }

    iFrameLoaded = false;
    iframe=document.createElement('iframe');
    iframe.onload = new Function('iFrameLoaded = true');
    iframe.src = 'blank_frame.html'; //Must use an HTML doc on the server because there is a very specific DOM structure that must be maintained.
    objectToAppendIFrameTo.appendChild(iframe);
    var it = 0;

    function checkiframe(){
        if(get_cookie('iframe_loaded')=="yes"){
            alert('iframe loaded');
            deleteCookie('iframe_loaded');
            return iframe;
        }else{
            setTimeout(checkiframe,1000);
        }
    }

    checkiframe();
}

このファイルでもフェールセーフ Cookie が削除されているためです。うまくいけば、それはあなたに何かを与えるでしょう:)

乾杯

G.

于 2011-01-02T00:40:13.687 に答える
0

ベースコードを変更できないのはなぜですか? たとえば、コア機能を次のように変更するのはかなり簡単です。

function unChangeableFunction()
{
    new_iFrame = onlyThingYouCanChange(document.getElementById('iFrameHolder'));
    new_iFrame_doc = (new_iFrame.contentWindow || new_iFrame.contentDocument);
    if(new_iFrame_doc.document)new_iFrame_doc=new_iFrame_doc.document;
    new_iFrame_body = new_iFrame_doc.body;
    if(new_iFrame_body.innerHTML != 'Loaded?')
    {
        //The world explodes!!!
        alert('you just blew up the world!  Way to go!');
    }
    else
    {
        alert('wow, you did it!  Way to go!');
    }
}

このようなものに:

function unChangeableFunction()
{
    var new_iFrame = onlyThingYouCanChange(document.getElementById('iFrameHolder'));
    new_iFrame.onload = function()
    {
        new_iFrame_doc = (new_iFrame.contentWindow || new_iFrame.contentDocument);
        if(new_iFrame_doc.document)new_iFrame_doc=new_iFrame_doc.document;
        new_iFrame_body = new_iFrame_doc.body;
        if(new_iFrame_body.innerHTML != 'Loaded?')
        {
            //The world explodes!!!
            alert('you just blew up the world!  Way to go!');
        }
        else
        {
            alert('wow, you did it!  Way to go!');
        }
    };
}

それがうまくいかない場合は、元のコードを透過的に変更してみませんか? これをJavascript Strandsでコンパイルし、組み込みの futures サポートを使用してこれを処理します。Javascript 1.7 も継続をサポートしていますが、それらを使用するには手動でコードを変更する必要があることに注意してください。

于 2011-01-02T00:16:59.810 に答える