162

I am writing a chrome extension. And I want to use jQuery in my extension. I am not using any background page, just a background script.

Here are my files :

manifest.json

{
    "manifest_version": 2,

    "name": "Extension name",
    "description": "This extension does something,",
    "version": "0.1",

    "permissions": [
        "activeTab"
    ],

    "browser_action": {
        "default_icon": "images/icon_128.png"
    },

    "background": {
        "scripts": ["background.js"],
        "persistent": false
    },

    "icons": {
        "16": "images/icon_16.png",
        "48": "images/icon_48.png",
        "128": "images/icon_128.png"
    }
}

My background.js file just runs another file named work.js

// Respond to the click on extension Icon
chrome.browserAction.onClicked.addListener(function (tab) {
    chrome.tabs.executeScript({
        file: 'work.js'
    });
});

The main logic of my extension is inside work.js. The contents of which I don't think matters here for this question.

What I want to ask is how can I use jQuery in my extension. Since I am not using any background page. I can't just add jQuery to it. So how can I add and use jQuery into my extension ?

I tried running jQuery along with my work.js from background.js file.

// Respond to the click on extension Icon
chrome.browserAction.onClicked.addListener(function (tab) {
    chrome.tabs.executeScript({
        file: 'thirdParty/jquery-2.0.3.js'
    });
    chrome.tabs.executeScript({
        file: 'work.js'
    });
});

And it works fine, but I am having the concern whether the scripts added to be executed in this manner are being executed asynchronously. If yes then it can happen that work.js runs even before jQuery (or other libraries which I may add in future).

And I would also like to know what's the correct and best way to use third party libraries, in my chrome extension.

Ishan
  • 3,303
  • 5
  • 29
  • 47
  • 3
    The correct way is to go vanilla! – bjb568 Dec 25 '14 at 00:06
  • If you are here looking for how to add jQuery to a pop-up extension (as I was), see this question: http://stackoverflow.com/questions/12035242/loading-jquery-into-chrome-extension – Pro Q Feb 11 '17 at 01:08

7 Answers7

147

You have to add your jquery script to your chrome-extension project and to the background section of your manifest.json like this :

  "background":
    {
        "scripts": ["thirdParty/jquery-2.0.3.js", "background.js"]
    }

If you need jquery in a content_scripts, you have to add it in the manifest too:

"content_scripts": 
    [
        {
            "matches":["http://website*"],
            "js":["thirdParty/jquery.1.10.2.min.js", "script.js"],
            "css": ["css/style.css"],
            "run_at": "document_end"
        }
    ]

This is what I did.

Also, if I recall correctly, the background scripts are executed in a background window that you can open via chrome://extensions.

Nico
  • 6,395
  • 4
  • 25
  • 34
  • 10
    Well what do you exactly mean by `You have to add your jquery script to your chrome-extension project` ? I did this : manifest.json : `"background": {` ` "scripts": ["thirdParty/jquery-2.0.3.js", "background.js"],` ` "persistent": false` ` },` and I have downloaded the jQuery to thirdParty folder. However I still can't use jQuery. It give the error : `Uncaught ReferenceError: $ is not defined ` my added this to my `work.js` file for testing. `$("body").html("Foo!");` – Ishan Jan 24 '14 at 06:47
  • The above comment looks like a mess but while adding comments preview is not shown. Please forgive me for that. – Ishan Jan 24 '14 at 06:54
  • I mean to add it to your chrome-extension folder. Like /home/you/chromexetension_source_files/thirdParty/jquery-2.0.3.js. You should do the same thing with your work.js. – Nico Jan 24 '14 at 15:56
  • 2
    I tried doing what you said. But I am still getting the same error of not being able to access jquery from my `work.js` file. `Uncaught ReferenceError: $ is not defined`. If you can, can you please upload a working example somewhere. Just a simple example like doing '$("body").html("Foo!");' in work.js. – Ishan Jan 25 '14 at 15:10
  • @ishan I can't really help you, this is what I did and I have jquery in my background.js script. – Nico Jan 27 '14 at 13:41
  • @ishan Have you tried to add `{"runAt":"document_end"}` in your `chrome.tabs.executeScript` call ? Like `chrome.tabs.executeScript({file: 'work.js', runAt:'document_end'})` – Nico Jan 27 '14 at 13:52
  • 13
    I also had trouble getting `jQuery` or `$` to be recognized. Turned out I was referencing jQuery last in the manifest array. When I put it first it was recognized. – BenR Jan 15 '15 at 18:42
  • in your chrome extension project folder , make your have thirdParty folder and you have jquery.1.10.2.min.js file in it (( thirdParty/jquery.1.10.2.min.js )) – user889030 Apr 06 '15 at 16:41
  • Page already has jQuery. Is there a way to use web site's jQuery? – alayli Jun 23 '15 at 00:31
  • @alayli I'm not sure about this but the background page is a page of it's own so it needs the lib. The content script should be able to use whatever is publicly available in the page it is injected. I think you don't necessarily need a background page so if you want your extension to depends on the injected page dependency, it's up to you. – Nico Jun 23 '15 at 14:54
  • @BenR - Your comment is truly a solution (at least for my needs with respect to getting a script loaded via `content_script` to find and use functions from the jQuery library). – D. Woods Sep 14 '17 at 21:11
  • in manifest V3 this manifest will give this error: ```The "background.scripts" key cannot be used with manifest_version 3. Use the "background.service_worker" key instead. Could not load manifest.``` so we have to use background.service_workers like this: ```background": {"service_workers": ["thirdParty/jquery-2.0.3.js", "background.js"]}``` – Manik Mar 13 '21 at 16:25
  • 1
    @Manik, for manifest v3, background service worker only works on one entry. Thus, adding a jquery in your example does not work. – Neilvert Noval Jan 30 '23 at 09:24
26

Its very easy just do the following:

add the following line in your manifest.json

"content_security_policy": "script-src 'self' https://ajax.googleapis.com; object-src 'self'",

Now you are free to load jQuery directly from url

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>

Source: google doc

Vikas Bansal
  • 10,662
  • 14
  • 58
  • 100
18

And it works fine, but I am having the concern whether the scripts added to be executed in this manner are being executed asynchronously. If yes then it can happen that work.js runs even before jQuery (or other libraries which I may add in future).

That shouldn't really be a concern: you queue up scripts to be executed in a certain JS context, and that context can't have a race condition as it's single-threaded.

However, the proper way to eliminate this concern is to chain the calls:

chrome.browserAction.onClicked.addListener(function (tab) {
    chrome.tabs.executeScript({
        file: 'thirdParty/jquery-2.0.3.js'
    }, function() {
        // Guaranteed to execute only after the previous script returns
        chrome.tabs.executeScript({
            file: 'work.js'
        });
    });
});

Or, generalized:

function injectScripts(scripts, callback) {
  if(scripts.length) {
    var script = scripts.shift();
    chrome.tabs.executeScript({file: script}, function() {
      if(chrome.runtime.lastError && typeof callback === "function") {
        callback(false); // Injection failed
      }
      injectScripts(scripts, callback);
    });
  } else {
    if(typeof callback === "function") {
      callback(true);
    }
  }
}

injectScripts(["thirdParty/jquery-2.0.3.js", "work.js"], doSomethingElse);

Or, promisified (and brought more in line with the proper signature):

function injectScript(tabId, injectDetails) {
  return new Promise((resolve, reject) => {
    chrome.tabs.executeScript(tabId, injectDetails, (data) => {
      if (chrome.runtime.lastError) {
        reject(chrome.runtime.lastError.message);
      } else {
        resolve(data);
      }
    });
  });
}

injectScript(null, {file: "thirdParty/jquery-2.0.3.js"}).then(
  () => injectScript(null, {file: "work.js"})
).then(
  () => doSomethingElse
).catch(
  (error) => console.error(error)
);

Or, why the heck not, async/await-ed for even clearer syntax:

function injectScript(tabId, injectDetails) {
  return new Promise((resolve, reject) => {
    chrome.tabs.executeScript(tabId, injectDetails, (data) => {
      if (chrome.runtime.lastError) {
        reject(chrome.runtime.lastError.message);
      } else {
        resolve(data);
      }
    });
  });
}

try {
  await injectScript(null, {file: "thirdParty/jquery-2.0.3.js"});
  await injectScript(null, {file: "work.js"});
  doSomethingElse();
} catch (err) {
  console.error(err);
}

Note, in Firefox you can just use browser.tabs.executeScript as it will return a Promise.

Xan
  • 74,770
  • 16
  • 179
  • 206
  • The first method is brilliant. As someone who doesn't know much JavaScript, I never considered something like that. – FriskySaga Mar 21 '20 at 01:15
14

Apart from the solutions already mentioned, you can also download jquery.min.js locally and then use it -

For downloading -

wget "https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"

manifest.json -

"content_scripts": [
   {
    "js": ["/path/to/jquery.min.js", ...]
   }
],

in html -

<script src="/path/to/jquery.min.js"></script>

Reference - https://developer.chrome.com/extensions/contentSecurityPolicy

Arik Pamnani
  • 421
  • 4
  • 7
4

Since manifest 3, anonymous functions are not allowed (neither in background). Jquery.js file usually has anonymous functions You should set a name to them.

Alberto Perez
  • 1,019
  • 15
  • 17
  • 2
    Is that going to pain-staking to go through the entire library to name them? Is there a way to automate this? – A13X Jul 29 '22 at 00:05
1

In my case got a working solution through Cross-document Messaging (XDM) and Executing Chrome extension onclick instead of page load.

manifest.json

{
  "name": "JQuery Light",
  "version": "1",
  "manifest_version": 2,

  "browser_action": {
    "default_icon": "icon.png"
  },

  "content_scripts": [
    {
      "matches": [
        "https://*.google.com/*"
      ],
      "js": [
        "jquery-3.3.1.min.js",
        "myscript.js"
      ]
    }
  ],

  "background": {
    "scripts": [
      "background.js"
    ]
  }

}

background.js

chrome.browserAction.onClicked.addListener(function (tab) {
  chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
    var activeTab = tabs[0];
    chrome.tabs.sendMessage(activeTab.id, {"message": "clicked_browser_action"});
  });
});

myscript.js

chrome.runtime.onMessage.addListener(
    function (request, sender, sendResponse) {
        if (request.message === "clicked_browser_action") {
        console.log('Hello world!')
        }
    }
);
Ruslan Novikov
  • 1,320
  • 15
  • 21
0

I downloaded jquery manually and imported it into the manifest file like a javascript file.

maifest.json

"content_scripts": [
    {
      "matches": [...],
      "js": ["jquery-3.6.0.min.js", ...],
    }
  ]

folder sructure

extension
│   manifest.json
│   jquery-3.6.0.min.js
|   ...
mariko
  • 119
  • 2
  • 7