web-dev-qa-db-ja.com

document.createElement( "script")同期的に

.jsファイルを同期的に呼び出して、その後すぐに使用することは可能ですか?

<script type="text/javascript">
    var head = document.getElementsByTagName('head').item(0);
    var script = document.createElement('script');
    script.setAttribute('type', 'text/javascript');
    script.setAttribute('src', 'http://mysite/my.js');
    head.appendChild(script);

    myFunction(); // Fails because it hasn't loaded from my.js yet.

    window.onload = function() {
        // Works most of the time but not all of the time.
        // Especially if my.js injects another script that contains myFunction().
        myFunction();
    };
</script>

これは簡単です。私の実装では、createElementスタッフは関数内にあります。制御を返す前に特定の変数がインスタンス化されているかどうかを確認できる関数に何かを追加することを考えました。しかし、その後、私が制御できない別のサイトからのjsを含めるときに何をすべきかという問題がまだあります。

考え?

編集:

何が起こっているのかについての良い説明を与えるので、私は今のところ最良の答えを受け入れました。しかし、誰かがこれを改善する方法について何か提案があれば、私は彼らにオープンです。これが私がやりたいことの例です。

// Include() is a custom function to import js.
Include('my1.js');
Include('my2.js');

myFunc1('blarg');
myFunc2('bleet');

内部を知りすぎず、「このモジュールを使用したいので、ここからいくつかのコードを使用します」と言うことができるようにしたいだけです。

71
Josh Johnson

これはきれいではありませんが、動作します:

<script type="text/javascript">
  document.write('<script type="text/javascript" src="other.js"></script>');
</script>

<script type="text/javascript">
  functionFromOther();
</script>

または

<script type="text/javascript">
  document.write('<script type="text/javascript" src="other.js"></script>');
  window.onload = function() {
    functionFromOther();
  };
</script>

スクリプトは、別の<script>タグに含めるか、window.onload()の前に含める必要があります。

これは機能しません:

<script type="text/javascript">
  document.write('<script type="text/javascript" src="other.js"></script>');
  functionFromOther(); // Error
</script>

Pointyと同じようにノードを作成しても同じことができますが、FFのみです。スクリプトが他のブラウザでいつ準備できるかは保証されません。

XMLの純粋主義者であることは本当に嫌いです。しかし、予想どおりに機能します。これらのいdocument.write()sを簡単にラップして、それらを見る必要がないようにすることができます。テストを行い、ノードを作成して追加し、document.write()にフォールバックすることもできます。

23
Josh Johnson

これはかなり遅いですが、これをやりたい人に将来参照するために、次を使用できます:

function require(file,callback){
    var head=document.getElementsByTagName("head")[0];
    var script=document.createElement('script');
    script.src=file;
    script.type='text/javascript';
    //real browsers
    script.onload=callback;
    //Internet Explorer
    script.onreadystatechange = function() {
        if (this.readyState == 'complete') {
            callback();
        }
    }
    head.appendChild(script);
}

少し前に短いブログ投稿をしました http://crlog.info/2011/10/06/dynamically-requireinclude-a-javascript-file-into-a-page-and-be-notified -when-its-loaded /

16
zcourts

非同期プログラミングはやや複雑です複雑です要求ステートメントに従う代わりに、関数にカプセル化されます。 ただし、ユーザーエクスペリエンスが可能なリアルタイム動作大幅に改善されました。サーバーやネットワークの停滞は見られないため、ブラウザーはクラッシュしたかのように動作します。 同期プログラミングは無礼および人が使用するアプリケーションでは使用しないでください

ダグラス・クロックフォードYUI Blog

さて、それはでこぼこの乗り物になるからです。 javascriptを使用してスクリプトを動的にロードすることを求める人が増えています。これはホットなトピックのようです。

これが非常に人気になった主な理由は次のとおりです。

  • クライアント側のモジュール性
  • より簡単な依存関係管理
  • エラー処理
  • パフォーマンスの利点

modularityについて:クライアント側の依存関係の管理は、クライアント側で処理する必要があることは明らかです。特定のオブジェクト、モジュール、またはライブラリが必要な場合は、それを要求して動的にロードします。

エラー処理:リソースに障害が発生した場合、影響を受けるスクリプトに依存する部分のみをブロックする機会が得られます。少し遅れます。

パフォーマンスはウェブサイト間で競争力のあるエッジになり、検索ランキング要素になりました。動的スクリプトでできることは、ブラウザーがスクリプトを処理する方法のデフォルトのブロック方法とは対照的に、非同期動作を模倣することです。 Scripts block他のリソース、scripts blockHTMLドキュメントのさらなる解析、 スクリプトブロックUI。ダイナミックスクリプトタグとそのクロスブラウザの代替により、実際の非同期リクエストを実行し、依存コードが利用可能な場合にのみ依存コードを実行できます。スクリプトは他のリソースと並行してロードされ、レンダリングは完璧になります。

一部の人々が同期スクリプトにこだわる理由は、同期スクリプトに慣れているためです。彼らはそれがデフォルトの方法であると考え、それはより簡単な方法であり、一部の人はそれが唯一の方法だとさえ考えるかもしれません。

ただし、アプリケーションの設計に関してこれを決定する必要がある場合に注意する必要があるのは、エンドユーザーエクスペリエンスだけです。そして、この分野では非同期を打ち負かすことはできません。ユーザーは即時の応答(またはpromiseと言います)を取得し、promiseは何もないよりも常に優れています。空白の画面は人々を怖がらせます。開発者は、パフォーマンスの向上のパフォーマンスを怠ることはできません。

そして最後に、汚い面についてのいくつかの言葉。ブラウザー間で機能させるために行うべきこと:

  1. 非同期的に考えることを学ぶ
  2. コードをモジュール式に整理する
  3. エラーとエッジケースを適切に処理するようにコードを整理する
  4. 漸進的に強化する
  5. 常に適切な量のフィードバックを処理する
7
gblazex

上記の答えは、正しい方向を示してくれました。ここに私が働いて得たものの一般的なバージョンがあります:

  var script = document.createElement('script');
  script.src = 'http://' + location.hostname + '/module';
  script.addEventListener('load', postLoadFunction);
  document.head.appendChild(script);

  function postLoadFunction() {
     // add module dependent code here
  }      
4
James

これは、動的なスクリプト読み込みのまともな概要のように見えます。 http://unixpapa.com/js/dyna.html

3
morgancodes

この質問に対する既存の回答(および他のスタックオーバーフロースレッドでのこの質問のバリエーション)には、次の問題がありました。

  • ロードされたコードのいずれもデバッグ可能でなかった
  • 多くのソリューションでは、真にブロックするのではなく、ロードがいつ終了したかを知るためにコールバックが必要でした。つまり、ロードされた(ロード中の)コードをすぐに呼び出すと実行エラーが発生します。

または、少し正確に:

  • ロードされたコードはいずれもデバッグ可能ではありませんでした(HTMLスクリプトタグブロックを除き、ソリューションがスクリプト要素をdomに追加した場合にのみ、個別の表示可能なスクリプトとしてではありません)=>ロード(およびデバッグ)する必要があるスクリプトの数を考えると、これは受け入れられませんでした。
  • 「onreadystatechange」または「onload」イベントを使用するソリューションはブロックに失敗しました。これは、コードが「require([filename、 'dojo/domReady']);」を使用して動的スクリプトを同期的にロードするため、大きな問題でした道場を外していた。

戻る前にスクリプトをロードし、デバッガーですべてのスクリプトに適切にアクセスできる最終的なソリューション(少なくともChromeの場合)は次のとおりです。

警告:次のコードは、おそらく「開発」モードでのみ使用する必要があります。(「リリース」モードの場合は、動的スクリプトのロードなしで、事前パッケージ化と縮小化を推奨します。少なくともevalなし)

//Code User TODO: you must create and set your own 'noEval' variable

require = function require(inFileName)
{
    var aRequest
        ,aScript
        ,aScriptSource
        ;

    //setup the full relative filename
    inFileName = 
        window.location.protocol + '//'
        + window.location.Host + '/'
        + inFileName;

    //synchronously get the code
    aRequest = new XMLHttpRequest();
    aRequest.open('GET', inFileName, false);
    aRequest.send();

    //set the returned script text while adding special comment to auto include in debugger source listing:
    aScriptSource = aRequest.responseText + '\n////# sourceURL=' + inFileName + '\n';

    if(noEval)//<== **TODO: Provide + set condition variable yourself!!!!**
    {
        //create a dom element to hold the code
        aScript = document.createElement('script');
        aScript.type = 'text/javascript';

        //set the script tag text, including the debugger id at the end!!
        aScript.text = aScriptSource;

        //append the code to the dom
        document.getElementsByTagName('body')[0].appendChild(aScript);
    }
    else
    {
        eval(aScriptSource);
    }
};
3
function include(file){
return new Promise(function(resolve, reject){
        var script = document.createElement('script');
        script.src = file;
        script.type ='text/javascript';
        script.defer = true;
        document.getElementsByTagName('head').item(0).appendChild(script);

        script.onload = function(){
        resolve()
        }
        script.onerror = function(){
          reject()
        }
      })

 /*I HAVE MODIFIED THIS TO  BE PROMISE-BASED 
   HOW TO USE THIS FUNCTION 

  include('js/somefile.js').then(function(){
  console.log('loaded');
  },function(){
  console.log('not loaded');
  })
  */
}
3
Daggie Blanqx

Webサイトに複数の.jsファイルがあり、それらが互いに依存していることに慣れています。それらをロードし、依存関係が正しい順序で評価されるようにするために、すべてのファイルをロードし、それらがすべて受信されると、eval()それらをロードする関数を作成しました。主な欠点は、CDNでは機能しないためです。このようなライブラリ(jQueryなど)の場合は、静的に含める方が適切です。 HTMLにスクリプトノードを挿入すると、動的は、少なくともChromeでなく、正しい順序でスクリプトが評価されることを保証しません。 )。

function xhrs(reqs) {
  var requests = [] , count = [] , callback ;

  callback = function (r,c,i) {
    return function () {
      if  ( this.readyState == 4 ) {
        if (this.status != 200 ) {
          r[i]['resp']="" ;
        } 
        else {
          r[i]['resp']= this.responseText ;
        }
        c[0] = c[0] - 1 ;
        if ( c[0] == 0 ) {
          for ( var j = 0 ; j < r.length ; j++ ) {
            eval(r[j]['resp']) ;
          }
        }
      }
    }
  } ;
  if ( Object.prototype.toString.call( reqs ) === '[object Array]' ) {
    requests.length = reqs.length ;
  }
  else {
    requests.length = 1 ;
    reqs = [].concat(reqs);
  }
  count[0] = requests.length ;
  for ( var i = 0 ; i < requests.length ; i++ ) {
    requests[i] = {} ;
    requests[i]['xhr'] = new XMLHttpRequest () ;
    requests[i]['xhr'].open('GET', reqs[i]) ;
    requests[i]['xhr'].onreadystatechange = callback(requests,count,i) ;
    requests[i]['xhr'].send(null);
  }
}

配列を作成せずに同じ値への参照を作成する方法がわかりません(カウント用)。そうでなければ、それは自明であると思います(すべてがロードされるとき、eval()与えられた順番ですべてのファイル、そうでなければ、単に応答を保存します)。

使用例:

xhrs( [
       root + '/global.js' ,
       window.location.href + 'config.js' ,
       root + '/js/lib/details.polyfill.min.js',
       root + '/js/scripts/address.js' ,
       root + '/js/scripts/tableofcontents.js' 
]) ;
1
user1251840

皮肉なことに、私はあなたが欲しいものを持っていますが、あなたが持っていたものにより近いものが欲しいです。

私は物事を動的および非同期に読み込みますが、loadコールバックを使用します(dojoとxmlhtpprequestを使用)

  dojo.xhrGet({
    url: 'getCode.php',
    handleAs: "javascript",
    content : {
    module : 'my.js'
  },
  load: function() {
    myFunc1('blarg');
  },
  error: function(errorMessage) {
    console.error(errorMessage);
  }
});

より詳細な説明については、 here を参照してください

問題は、コードが回避される行のどこかであり、コードに問題がある場合、console.error(errorMessage);ステートメントは実際のエラーではなくeval()がある行を示します。これは非常に大きな問題であり、実際に<script>ステートメントに変換しようとしています( here を参照)。

0
puk