views:

179

answers:

3

I load this JS code from a bookmarklet:

function in_array(a, b)
{
  for (i in b)
    if (b[i] == a)
      return true;
  return false;
}

function include_dom(script_filename) {
    var html_doc = document.getElementsByTagName('head').item(0);
    var js = document.createElement('script');
    js.setAttribute('language', 'javascript');
    js.setAttribute('type', 'text/javascript');
    js.setAttribute('src', script_filename);
    html_doc.appendChild(js);
    return false;
}

var itemname = '';
var currency = '';
var price    = '';

var supported = new Array('www.amazon.com');
var domain = document.domain;

if (in_array(domain, supported))
{
  include_dom('http://localhost/bklts/parse/'+domain+'.js');
  alert(getName());
}
[...]

Note that the 'getName()' function is in http://localhost/bklts/parse/www.amazon.com/js. This code works only the -second- time I click the bookmarklet (the function doesn't seem to get loaded until after the alert()).

Oddly enough, if I change the code to:

if (in_array(domain, supported))
{
  include_dom('http://localhost/bklts/parse/'+domain+'.js');
  alert('hello there');
  alert(getName());
}

I get both alerts on the first click, and the rest of the script functions. How can I make the script work on the first click of the bookmarklet without spurious alerts?

Thanks! -Mala

A: 

It sounds like the loading of the external script (http://localhost/bklts/parse/www.amazon.com/js) isn't blocking execution until it is loaded. A simple timeout might be enough to give the browser a chance to update the DOM and then immediately queue up the execution of your next block of logic:

//...
if (in_array(domain, supported))
{
    include_dom('http://localhost/bklts/parse/'+domain+'.js');
    setTimeout(function() {
        alert(getName());
    }, 0);
}
//...

In my experience, if zero doesn't work for the timeout amount, then you have a real race condition. Making the timeout longer (e.g. 10-100) may fix it for some situations but you get into a risky situation if you need this to always work. If zero works for you, then it should be pretty solid. If not, then you may need to push more (all?) of your remaining code to be executed into the external script.

McKAMEY
I'm no javascript guru, but maybe you could set a `loaded=false` before `include_dom(...)` then at the end of the external set `loaded=true` then after do a `while` that waits for it?
Austin Hyde
A timeout of 100 still doesn't make it work, in spite of the script currently being hosted locally - so once it's online any reasonable amount of time probably won't work either. Using the loaded=false idea might work, trying it out now...
Mala
Austin - tried your idea but it throws me into an infinite loop that makes the browser hang until firefox decides to stop the execution... I suppose it's time to try pushing all the remaining code into the external function :-\ Gh!
Mala
McKamey, putting the rest of the code in the external script doesn't work either - having just alert("hi"); in the external script does not show the popup.My (albeit messy) solution is to have a php script output the JS code, pass the domain to the php script, and have -it- include the file.
Mala
if `alert("hi");` isn't executing the external script, then I'd question whether it is even properly loading. I'd verify your script next. if you change `include_dom('http://localhost/bklts/parse/'+domain+'.js');` to `window.open('http://localhost/bklts/parse/'+domain+'.js');` does a window popup with the correct script in it?
McKAMEY
[the comment stripped the http : / /]
McKAMEY
it was loading properly as far as i could tell. In any case, I changed it to my answer below, which has the added bonus of one fewer tcp requests. Thanks for your ideas though :)
Mala
A: 

The best way I could get working: Don't.

Since I was calling the JS from a small loader bookmarklet anyway (which just tacks the script on to the page you're looking at) I modified the bookmarklet to point the src to a php script which outputs the JS code, taking the document.domain as a parameter. As such, I just used php to include the external code.

Hope that helps someone. Since it's not really an answer to my question, I won't mark this as the accepted answer. If someone has a better way, I'd love to know it, but I'll be leaving my code as is:

bookmarklet:

javascript:(function(){document.body.appendChild(document.createElement('script')).src='http://localhost/bklts/div.php?d='+escape(document.domain);})();

localhost/bklts/div.php:

<?php

print("
// JS code
");

$supported = array("www.amazon.com", "www.amazon.co.uk");
$domain = @$_GET['d']
if (in_array($domain, $supported))
    include("parse/$domain.js");

print("
// more JS code
");

?>
Mala
+2  A: 

Adding a <script> tag through DHTML makes the script load asynchroneously, which means that the browser will start loading it, but won't wait for it to run the rest of script.

You can handle events on the tag object to find out when the script is loaded. Here is a piece of sample code I use that seems to work fine in all browsers, although I'm sure theres a better way of achieving this, I hope this should point you in the right direction:

Don't forget to change tag to your object holding the <script> element, fnLoader to a function to call when the script is loaded, and fnError to a function to call if loading the script fails.

Bear in mind that those function will be called at a later time, so they (like tag) must be available then (a closure would take care of that normally).

     tag.onload = fnLoader;
 tag.onerror = fnError;
 tag.onreadystatechange = function() {
  if (!window.opera && typeof tag.readyState == "string"){
   /* Disgusting IE fix */
   if (tag.readyState == "complete" || tag.readyState == "loaded") {
    fnLoader();
   } else if (tag.readyState != "loading") {
    fnError();
   };
  } else if (tag.readyState == 4) {
   if (tag.status != 200) {
    fnLoader();
   }
   else {
    fnError();
   };
  };
 });
Eric
thanks, this seems to work exactly how i wanted!
Mala