Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
469 views
in Technique[技术] by (71.8m points)

dom events - Dynamically load external Javascript file, and wait for it to load - without using JQuery

I'm trying to dynamically load an external .js file (according to an input I get from the user). Currently my code looks like this:

function createScript(src, id) {
    if (document.getElementById(id) == null) {
        var newScript = document.createElement('script');
        newScript.setAttribute("type", "text/javascript");
        newScript.setAttribute("src", src);
        newScript.setAttribute("id", id);
        document.getElementsByTagName("head")[0].appendChild(newScript);
    }
}

This kind of work. It does load the script, but if I try to call a function that is defined inside the new script it won't work. The weird thing is if I wait for a second (using an alert("test") or something like that) the script does work.

I guess its because the browser adds the <script> tag immediately, but it takes it a little longer to "read" the script inside of the .js file. I tried playing with "typeof testfunction === 'function'" with a few variations but no luck.

Any good way of doing this?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

February 21, 2017 - how jQuery does it

jQuery.getScript is now just a wrapper for jQuery.get

getScript: function( url, callback ) {
    return jQuery.get( url, undefined, callback, "script" );
}

jQuery.get is just a wrapper of jQuery.ajax – it is defined using metaprogramming as ...

jQuery.each( [ "get", "post" ], function( i, method ) {
    jQuery[ method ] = function( url, data, callback, type ) {

        // Shift arguments if data argument was omitted
        if ( jQuery.isFunction( data ) ) {
            type = type || callback;
            callback = data;
            data = undefined;
        }

        // The url can be an options object (which then must have .url)
        return jQuery.ajax( jQuery.extend( {
            url: url,
            type: method,
            dataType: type,
            data: data,
            success: callback
        }, jQuery.isPlainObject( url ) && url ) );
    };
} );

jQuery.ajax is this 430+ LOC monster

ajax: function( url, options ) {

  // If url is an object, simulate pre-1.5 signature
  if ( typeof url === "object" ) {
    options = url;
    url = undefined;
  }

  // Force options to be an object
  options = options || {};

  var transport,

    // URL without anti-cache param
    cacheURL,

    // Response headers
    responseHeadersString,
    responseHeaders,

    // timeout handle
    timeoutTimer,

    // Url cleanup var
    urlAnchor,

    // Request state (becomes false upon send and true upon completion)
    completed,

    // To know if global events are to be dispatched
    fireGlobals,

    // Loop variable
    i,

    // uncached part of the url
    uncached,

    // Create the final options object
    s = jQuery.ajaxSetup( {}, options ),

    // Callbacks context
    callbackContext = s.context || s,

    // Context for global events is callbackContext if it is a DOM node or jQuery collection
    globalEventContext = s.context &&
      ( callbackContext.nodeType || callbackContext.jquery ) ?
        jQuery( callbackContext ) :
        jQuery.event,

    // Deferreds
    deferred = jQuery.Deferred(),
    completeDeferred = jQuery.Callbacks( "once memory" ),

    // Status-dependent callbacks
    statusCode = s.statusCode || {},

    // Headers (they are sent all at once)
    requestHeaders = {},
    requestHeadersNames = {},

    // Default abort message
    strAbort = "canceled",

    // Fake xhr
    jqXHR = {
      readyState: 0,

      // Builds headers hashtable if needed
      getResponseHeader: function( key ) {
        var match;
        if ( completed ) {
          if ( !responseHeaders ) {
            responseHeaders = {};
            while ( ( match = rheaders.exec( responseHeadersString ) ) ) {
              responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ];
            }
          }
          match = responseHeaders[ key.toLowerCase() ];
        }
        return match == null ? null : match;
      },

      // Raw string
      getAllResponseHeaders: function() {
        return completed ? responseHeadersString : null;
      },

      // Caches the header
      setRequestHeader: function( name, value ) {
        if ( completed == null ) {
          name = requestHeadersNames[ name.toLowerCase() ] =
            requestHeadersNames[ name.toLowerCase() ] || name;
          requestHeaders[ name ] = value;
        }
        return this;
      },

      // Overrides response content-type header
      overrideMimeType: function( type ) {
        if ( completed == null ) {
          s.mimeType = type;
        }
        return this;
      },

      // Status-dependent callbacks
      statusCode: function( map ) {
        var code;
        if ( map ) {
          if ( completed ) {

            // Execute the appropriate callbacks
            jqXHR.always( map[ jqXHR.status ] );
          } else {

            // Lazy-add the new callbacks in a way that preserves old ones
            for ( code in map ) {
              statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
            }
          }
        }
        return this;
      },

      // Cancel the request
      abort: function( statusText ) {
        var finalText = statusText || strAbort;
        if ( transport ) {
          transport.abort( finalText );
        }
        done( 0, finalText );
        return this;
      }
    };

  // Attach deferreds
  deferred.promise( jqXHR );

  // Add protocol if not provided (prefilters might expect it)
  // Handle falsy url in the settings object (#10093: consistency with old signature)
  // We also use the url parameter if available
  s.url = ( ( url || s.url || location.href ) + "" )
    .replace( rprotocol, location.protocol + "//" );

  // Alias method option to type as per ticket #12004
  s.type = options.method || options.type || s.method || s.type;

  // Extract dataTypes list
  s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ];

  // A cross-domain request is in order when the origin doesn't match the current origin.
  if ( s.crossDomain == null ) {
    urlAnchor = document.createElement( "a" );

    // Support: IE <=8 - 11, Edge 12 - 13
    // IE throws exception on accessing the href property if url is malformed,
    // e.g. http://example.com:80x/
    try {
      urlAnchor.href = s.url;

      // Support: IE <=8 - 11 only
      // Anchor's host property isn't correctly set when s.url is relative
      urlAnchor.href = urlAnchor.href;
      s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !==
        urlAnchor.protocol + "//" + urlAnchor.host;
    } catch ( e ) {

      // If there is an error parsing the URL, assume it is crossDomain,
      // it can be rejected by the transport if it is invalid
      s.crossDomain = true;
    }
  }

  // Convert data if not already a string
  if ( s.data && s.processData && typeof s.data !== "string" ) {
    s.data = jQuery.param( s.data, s.traditional );
  }

  // Apply prefilters
  inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );

  // If request was aborted inside a prefilter, stop there
  if ( completed ) {
    return jqXHR;
  }

  // We can fire global events as of now if asked to
  // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
  fireGlobals = jQuery.event && s.global;

  // Watch for a new set of requests
  if ( fireGlobals && jQuery.active++ === 0 ) {
    jQuery.event.trigger( "ajaxStart" );
  }

  // Uppercase the type
  s.type = s.type.toUpperCase();

  // Determine if request has content
  s.hasContent = !rnoContent.test( s.type );

  // Save the URL in case we're toying with the If-Modified-Since
  // and/or If-None-Match header later on
  // Remove hash to simplify url manipulation
  cacheURL = s.url.replace( rhash, "" );

  // More options handling for requests with no content
  if ( !s.hasContent ) {

    // Remember the hash so we can put it back
    uncached = s.url.slice( cacheURL.length );

    // If data is available, append data to url
    if ( s.data ) {
      cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data;

      // #9682: remove data so that it's not used in an eventual retry
      delete s.data;
    }

    // Add or update anti-cache param if needed
    if ( s.cache === false ) {
      cacheURL = cacheURL.replace( rantiCache, "$1" );
      uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached;
    }

    // Put hash and anti-cache on the URL that will be requested (gh-1732)
    s.url = cacheURL + uncached;

  // Change '%20' to '+' if this is encoded form body content (gh-2658)
  } else if ( s.data && s.processData &&
    ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) {
    s.data = s.data.replace( r20, "+" );
  }

  // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
  if ( s.ifModified ) {
    if ( jQuery.lastModified[ cacheURL ] ) {
      jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
    }
    if ( jQuery.etag[ cacheURL ] ) {
      jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
    }
  }

  // Set the correct header, if data is being sent
  if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
    jqXHR.setRequestHeader( "Content-Type", s.contentType );
  }

  // Set the Accepts header for the server, depending on the dataType
  jqXHR.setRequestHeader(
    "Accept",
    s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ?
      s.accepts[ s.dataTypes[ 0 ] ] +
        ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
      s.accepts[ "*" ]
  );

  // Check for headers option
  for ( i in s.headers ) {
    jqXHR.setRequestHeader( i, s.headers[ i ] );
  }

  // Allow custom headers/mimetypes and early abort
  if ( s.beforeSend &&
    ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) {

    // Abort if not done already and return
    return jqXHR.abort();
  }

  // Aborting is no longer a cancellation
  strAbort = "abort";

  // Install callbacks on deferreds
  completeDeferred.add( s.complete );
  jqXHR.done( s.success );
  jqXHR.fail( s.error );

  // Get transport
  transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );

  // If no transport, we auto-abort
  if ( !transport ) {
    done( -1, "No Transport" );
  } else {
    jqXHR.readyState = 1;

    // Send global event
    if ( fireGlobals ) {
      globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
    }

    // If request was aborted inside ajaxSend, stop there
    if ( completed ) {
      return jqXHR;
    }

    // Timeout
    if ( s.async && s.timeout > 0 ) {
      timeoutTimer = window.setTimeout( function() {
        jqXHR.abort( "timeout" );
      }, s.timeout );
    }

    try {
      completed = false;
      transport.send( requestHeaders, done );
    } catch ( e ) {

      // Rethrow post-completion exceptions
      if ( completed ) {
        throw e;
      }

      // Propagate others as results
      done( -1, e );
    }
  }

  // Callback for when everything is done
  function done( status, nativeStatusText, responses, headers ) {
    var isSuccess, success, error, response, modified,
      statusText = nativeStatusText;

    // Ignore repeat invocations
    if ( completed ) {
      return;
    }

    completed = true;

    // Clear timeout if it exists
    if ( timeoutTimer ) {
      window.clearTimeout( timeoutTimer );
    }

    // Dereference transport for early garbage collection
    // (no matter how long the jqXHR object will be used)
    transport = undefined;

    // Cache response headers
    responseHeadersString = headers || "";

    // Set readyState
    jqXHR.readyState = status > 0 ? 4 : 0;

    // Determine if successful
    isSuccess = status >= 200 && status < 300 || status === 304;

    // Get 

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...