57

I am building a Chrome Extension and I have a requirement to overlay a blob of html on top of a few websites. At the moment I am using a JQuery .Get to pull the html from my server. In order to improve performance I am wondering if it is possible to include the html as a file in the extension directory and access the source directly from there? Does anyone know if this is possible?

UPDATE

Rob's suggestion does the job (see accepted answer). The only additional step is to register the file in the manifest under web_accessible_resources.

{
   ...
   "web_accessible_resources": [
       "myimportfile1.html",
       "myimportfile2.html"
    ],
    ...
}
QFDev
  • 8,668
  • 14
  • 58
  • 85

5 Answers5

79

Yes, that's possible. Use chrome.runtime.getURL to get an absolute URL for the resource. For example:

Step 1 (standard JavaScript):

fetch(chrome.runtime.getURL('/template.html')).then(r => r.text()).then(html => {
  document.body.insertAdjacentHTML('beforeend', html);
  // not using innerHTML as it would break js event listeners of the page
});

Step 1 (jQuery):

$.get(chrome.runtime.getURL('/template.html'), function(data) {
    $(data).appendTo('body');
    // Or if you're using jQuery 1.8+:
    // $($.parseHTML(data)).appendTo('body');
});

Step 2:

Register the resource in the manifest.json under web_accessible_resources:

  "web_accessible_resources": [
    "template.html",
    "foo.jpg"
  ]
Rob W
  • 341,306
  • 83
  • 791
  • 678
  • 7
    Works perfectly, thanks! The only additional step is to register the resource in the manifest under web_accessible_resources: http://developer.chrome.com/extensions/manifest.html#web_accessible_resources – QFDev May 02 '13 at 14:07
  • 1
    @Chamnap well in my case I had this code in my content script. – QFDev Dec 05 '15 at 12:50
  • 1
    @Vidit Put the JS in the content script. If not required, then you should avoid injecting scripts in the web page. – Rob W Apr 27 '16 at 08:09
  • @ViditSaxena A frame or shadow DOM would offer much better isolation from the page than directly inserting the element in the page. But please read the existing questions and answers about this topic instead of adding more unrelated commentary to this answer. If it turns out that your question isn't covered anywhere, then you can post a new question, and others with the same question can find the Q&A more easily. – Rob W Apr 28 '16 at 14:35
  • Do you know why I receieve "$ is not defined" – codertryer Jan 06 '21 at 11:48
  • @codertryer have you definitely included jQuery in the page? It'll be something to do with jQuery library not being available. You could try using `jQuery` instead of `$` incase your application is using that reference? – hobailey May 18 '22 at 16:46
23

Another way of doing it is to use new Fetch API:

If the file's name is modal.html - update manifest.json accordingly

    "web_accessible_resources": [
        "modal.html",
    ],

and inject it like this:

    fetch(chrome.runtime.getURL('/modal.html'))
        .then(response => response.text())
        .then(data => {
            document.getElementById('inject-container').innerHTML = data;
            // other code
            // eg update injected elements,
            // add event listeners or logic to connect to other parts of the app
        }).catch(err => {
            // handle error
        });
Be Kind
  • 4,712
  • 1
  • 38
  • 45
6

This is my approach using a synchronous XHR:

var xmlHttp = null;

xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", chrome.runtime.getURL ("src/inject/inject.html"), false );
xmlHttp.send( null );
    
var inject  = document.createElement("div");
inject.innerHTML = xmlHttp.responseText
document.body.insertBefore (inject, document.body.firstChild);

Without jQuery etc.

wOxxOm
  • 65,848
  • 11
  • 132
  • 136
Kamil
  • 1,633
  • 2
  • 21
  • 24
  • looks like Chrome Browser has deprecated XMLHttpRequest() on the main thread. This is what I am getting when I adopted this code for the browserAction.click method: `Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check http://xhr.spec.whatwg.org/.` – baskint Oct 28 '15 at 15:24
  • 2
    change last parameter of `.open()` to `true` and it will be asynchronous – Kamil Oct 28 '15 at 18:35
  • 1
    To make asynchronous, I had to change the last param of `.open()` to `true` and wrap the last three lines of code in `xmlHttp.onload = function () { ... }` – richardkmiller Aug 09 '17 at 13:52
6

I use this code. It's only 3 lines of code and you don't need any jquery's garbage.

var iframe  = document.createElement ('iframe');
iframe.src  = chrome.runtime.getURL ('iframe.html');
document.body.appendChild (iframe);
wOxxOm
  • 65,848
  • 11
  • 132
  • 136
  • Nice answer, at the (low) cost of rendering searches a little bit longer (`document.getElementById("myFrame").contentWindow.document.getElementById("whatever");`) – Joël Sep 12 '19 at 12:59
  • Mm, just found out it also poses problem with CORS: https://stackoverflow.com/questions/3076414/ways-to-circumvent-the-same-origin-policy, that are not so trivial to circumvent. – Joël Sep 12 '19 at 13:08
2

If you're using Angular in your Chrome extension, you can make use of ng-include

var injectedContent = document.createElement("div");
injectedContent.setAttribute("ng-include", "");
//ng-include src value must be wrapped in single quotes
injectedContent.setAttribute("src", "'" + chrome.runtime.getURL("template.html") + "'");
existingElement.appendChild(injectedContent);
wOxxOm
  • 65,848
  • 11
  • 132
  • 136
WeNeigh
  • 3,489
  • 3
  • 27
  • 40