58

I want to use jQuery inside a firefox extension, I imported the library in the xul file like this:

<script type="application/x-javascript" src="chrome://myExtension/content/jquery.js"> </script>

but the $() function is not recognized in the xul file neither do the jQuery().

I googled about the problem and found some solutions but no one did work with me: http://gluei.com/blog/view/using-jquery-inside-your-firefox-extension http://forums.mozillazine.org/viewtopic.php?f=19&t=989465

I've also tried to pass the 'content.document' object(which refrences the 'document' object) as the context parameter to the jQuery function like this:

$('img',content.document);

but still not working, does any one came across this problem before?

Michael Paulukonis
  • 9,020
  • 5
  • 48
  • 68
  • Id offer a bounty on this, interesting – DFectuoso Jan 29 '09 at 18:37
  • Read in detail about using jquery inside firefox extension safely with no conflicts with other extension and other XUL components like toolbar buttons etc, here : http://meherranjan.com/blog/a-guide-to-using-jquery-inside-firefox-extension/ – Meher Ranjan Nov 03 '10 at 19:49

7 Answers7

28

I use the following example.xul:

<?xml version="1.0"?>
<overlay id="example" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<head></head>
<script type="application/x-javascript" src="jquery.js"></script>
<script type="application/x-javascript" src="example.js"></script>
</overlay>

And here is an example.js

(function() {
    jQuery.noConflict();
    $ = function(selector,context) { 
        return new jQuery.fn.init(selector,context||example.doc); 
    };
    $.fn = $.prototype = jQuery.fn;

    example = new function(){};
    example.log = function() { 
        Firebug.Console.logFormatted(arguments,null,"log"); 
    };
    example.run = function(doc,aEvent) {
        // Check for website
        if (!doc.location.href.match(/^http:\/\/(.*\.)?stackoverflow\.com(\/.*)?$/i))  
            return;

        // Check if already loaded
        if (doc.getElementById("plugin-example")) return;

        // Setup
        this.win = aEvent.target.defaultView.wrappedJSObject;
        this.doc = doc;

        // Hello World
        this.main = main = $('<div id="plugin-example">').appendTo(doc.body).html('Example Loaded!');
        main.css({ 
            background:'#FFF',color:'#000',position:'absolute',top:0,left:0,padding:8
        });
        main.html(main.html() + ' - jQuery <b>' + $.fn.jquery + '</b>');
    };

    // Bind Plugin
    var delay = function(aEvent) { 
        var doc = aEvent.originalTarget; setTimeout(function() { 
            example.run(doc,aEvent); 
        }, 1); 
     };
    var load = function() { 
        gBrowser.addEventListener("DOMContentLoaded", delay, true); 
    };
    window.addEventListener("pageshow", load, false);

})();
Wayne
  • 59,728
  • 15
  • 131
  • 126
sunsean
  • 2,818
  • 1
  • 17
  • 5
  • that doesn't work also, please if you know any extension that use jQuery give me its name to check the code. –  Jan 30 '09 at 20:48
  • I've added a more complete example. Check and see if that works for you. – sunsean Feb 02 '09 at 22:02
  • 3
    This method is absolutely inappropriate as when you load jquery from overflow then the 2 objects jQuery and $ are defined for the entire window and might cause conflict with other extensions. Though you can use jquery.noconflict and function scope to hide it but overlays can be loaded asynchronously. There is still a possibility that another firefox's extensions has been already defined and used jQuery and $ on the window before I can use jquery.noconflict – Meher Ranjan Oct 25 '10 at 13:27
  • what is the purpose of the delay function? – Alexis Nov 16 '10 at 20:20
  • This answer has multiple flaws. However I felt that editing it to fix it would be too radical of an edit. Instead http://stackoverflow.com/a/23776848/484441 – nmaier May 21 '14 at 07:35
10

The following solution makes it possibile to use jQuery in contentScriptFile (Targetting 1.5 Addon-sdk)

In your main.js:

exports.main = function() {
    var pageMod = require("page-mod");

    pageMod.PageMod({
          include: "*",
          contentScriptWhen: 'end',
          contentScriptFile: [data.url("jquery-1.7.1-min.js") , data.url("notifier.js") ,   data.url("message.js")],
          onAttach: function onAttach(worker) {
             //show the message
             worker.postMessage("Hello World");
          }
    });

};

In your message.js :

self.on("message", function(message){
    if(message !== "undefined"){
       Notifier.info(message); 
    }
});

Some pitfalls you need to watchs out for:

  • The order of the contentScriptFile array. if message.js would be placed first: jQuery won't be reconized.
  • Do not place a http:// url in the data.url (this does not work)!
  • All your javascript files should be in the data folder. (only main.js should be in lib folder)
David
  • 4,185
  • 2
  • 27
  • 46
3

Turns out the current top-answer by @sunsean does not work as expected when it comes to handling multiple loads. The function should properly close over the document and avoid global state.

Also, you have to call jQuery.noConflict(true) to really avoid conflicts with other add-ons!

This is who I would write it (then again, I would avoid jquery (in add-ons) like the plague...). First the overlay XUL

<?xml version="1.0"?>
<overlay id="test-addon-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <script type="text/javascript" src="jquery.js"/>
  <script type="text/javascript" src="overlay.js"/>
</overlay>

And then the overlay script:

// Use strict mode in particular to avoid implicitly var declarations
(function() {
  "use strict";

  // Main runner function for each content window.
  // Similar to SDK page-mod, but without the security boundaries.
  function run(window, document) {
    // jquery setup. per https://stackoverflow.com/a/496970/484441
    $ = function(selector,context) {
      return new jq.fn.init(selector,context || document); 
    };
    $.fn = $.prototype = jq.fn;

    if (document.getElementById("my-example-addon-container"))  {
      return;
    }
    let main = $('<div id="my-example-addon-container">');
    main.appendTo(document.body).text('Example Loaded!');
    main.click(function() { //<--- added this function
      main.text(document.location.href);
    });
    main.css({
      background:'#FFF',color:'#000',position:'absolute',top:0,left:0,padding:8
    });
  };

  const log = Components.utils.reportError.bind(Components.utils);

  // Do not conflict with other add-ons using jquery.
  const jq = jQuery.noConflict(true);

  gBrowser.addEventListener("DOMContentLoaded", function load(evt) {
    try {
      // Call run with this == window ;)
      let doc = evt.target.ownerDocument || evt.target;
      if (!doc.location.href.startsWith("http")) {
        // Do not even attempt to interact with non-http(s)? sites.
        return;
      }
      run.call(doc.defaultView, doc.defaultView, doc);
    }
    catch (ex) {
      log(ex);
    }
  }, true);
})();

Here is a complete add-on as a gist. Just drop in a copy of jquery and it should be good to go.

Community
  • 1
  • 1
nmaier
  • 32,336
  • 5
  • 63
  • 78
3

There is an excellent article in the mozillaZine forums that describes this step-by-step: http://forums.mozillazine.org/viewtopic.php?f=19&t=2105087

I haven't tried it yet, though so I hesitate to duplicate the info here.

Skrud
  • 11,604
  • 5
  • 24
  • 22
2

I think this is what Eric was saying, but you can load Javascript from the URL directly.

javascript:var%20s=document.createElement('script');s.setAttribute('src','http://YOURJAVASCRIPTFILE.js');document.getElementsByTagName('body')[0].appendChild(s);void(s);

Im assuming you want your extension to load JQuery so you can manipulate the page elements easily? My company's labs has something that does this using Javascript directly here: http://parkerfox.co.uk/labs/pixelperfect

Craig Stanford
  • 1,803
  • 1
  • 13
  • 8
  • I know how to inject the library to the DOM of the page, but the problem is that I can't inject all the function of my extension to the DOM Whenever a page is loaded. that is why I want all the function to be on the extension folder –  Jan 30 '09 at 18:39
0

Instead of

$('img',content.document);

you can try

$('img',window.content.document);

In my case it works.

0

It may be bad practice, but have you considered including it inline?

Eric Wendelin
  • 43,147
  • 9
  • 68
  • 92
  • I didn't try this but I don't think it is different from including it in the script tag. I will try anyway –  Jan 30 '09 at 18:39
  • They will not approve your extension if you do that. You have to include the original unmodified library files, and the Mozilla approver will verify that the MD5/SHA checksums match. – Jay Taylor Apr 20 '11 at 21:51