7

My bookmarklet consist of a call to a 'launcher' script that is inserted into body. As it runs, it inserts in a similar way more scripts (jQuery, the actual application), and a CSS.

Bookmarklet

javascript:(function(){
    var l = document.createElement('script');
    l.setAttribute('type','text/javascript');
    l.setAttribute('src','launcher.js');
    document.body.appendChild(l);
})();

Launcher.js

var s = document.createElement('script');
s.setAttribute('type','text/javascript');
s.setAttribute('src','my actual script');
document.body.appendChild(s);

// I repeat a block like this once per script/css.

The problem is that if the bookmarklet is clicked twice, all the scripts will be inserted again. How can I prevent this behavior?

deprecated
  • 5,142
  • 3
  • 41
  • 62

3 Answers3

4

You can set a property on the window object which is oddly named, so it is unlikely to cause namespace collisions, on the first run only. On subsequent runs, the code will not execute if the property is present:

    javascript:(function(){
      // Test for existence of your weird property
      if (!window.hasOwnProperty('yourWeirdPropertyX774x9ZZYy4')) {

          // Run all your existing code
          var l = document.createElement('script');
          l.setAttribute('type','text/javascript');
          l.setAttribute('src','launcher.js');
          document.body.appendChild(l);

         // Set the flag so this won't run again
         window.yourWeirdPropertyX774x9ZZYy4 = true;
      }  
    })();
Michael Berkowski
  • 267,341
  • 46
  • 444
  • 390
  • oops - edited to fix wonky syntax and place the if block since the anon func rather than nonsensically around it. – Michael Berkowski Sep 10 '11 at 20:04
  • Perfect - my previous attempts were certainly along these lines but I was using variables instead window props, which I totally didn't know about. Thanks! – deprecated Sep 10 '11 at 20:26
  • 1
    @vemv A variable declared inside a function as `yourVar = 1234;` without the `var` keyword in front of it does in fact become a property of the global `window` object, but this method is much more explicit and deliberate. – Michael Berkowski Sep 10 '11 at 20:31
3

I found the cleanest way for me to do this was to name the function that loads the external js file, the same name as a function inside the external file that I want to be ran everytime the user clicks the bookmarklet. This way when the bookmarklet is clicked the first time, it loads the external file and on subsquent clicks a function in the external file is ran.

Here is some sudo code:

This would be the code in the url of the bookmarklet link

if( ! bookmarklet){
    var bookmarklet = {
     click: function(){
         js = document.createElement('SCRIPT');
         js.type = 'text/javascript';
         js.src = 'http://external.js?' + (Math.random());
         document.getElementsByTagName('head')[0].appendChild(js);
     }
   };
}
bookmarklet.click(); // this will be called everytime the bookmarklet is clicked

The external.js file would contain a function with the same name like this:

var bookmarklet = {
    click:function(){
         // insert logic you want to run every time the bookmarklet is clicked
    }
};
bookmarket.click(); // run once after file is loaded

This way when the external file is loaded your function (example function is bookmarklet.click) now runs the function inside the externally loaded file and will not load the external js file a second time.

chuckles
  • 226
  • 1
  • 7
0

Simplest/Cleanest way to implement singleton in JavaScript?

An example on a good way to implement Singletons in JS. I won't copy/paste his answer, but give it a good read :)

Community
  • 1
  • 1
Joe
  • 15,669
  • 4
  • 48
  • 83
  • Maybe using the word 'singleton' was a mistake of mine - I don't think enclosing the app into a object literal will solve this. – deprecated Sep 10 '11 at 19:39