How to Inject HTML along with CSS and JS in a single HTML file? - Chrome Extension

3.1k views Asked by At

My extension is intended to inject some HTML elements using local JavaScript libraries such PrimeUI, jQueryUI and its correspondent CSS stylesheets.

The best way I have found to do so, is to inject the HTML as follows:

// test.js
$.get(chrome.extension.getURL('/overlay.html'), function(data) {
    $($.parseHTML(data)).appendTo('body');
}
// manifest.json
{
"name": "Test",
"version": "1.0",
"manifest_version": 2,
"options_page": "",
"description": "Test",
"browser_action": {
  "default_title": "Test",
  "default_popup": ""
},
"content_scripts": [
{
  "matches": [
    "<all_urls>"
  ],
  "js": [
    "js/jquery-3.1.1.min.js",
    "js/test.js"
  ]
}
],
"permissions": [
  "tabs"
],
"web_accessible_resources": [
  "overlay.html",

  "css/font-awesome.min.css",
  "css/jquery-ui.min.css",
  "css/primeui-all.min.css",

  "js/jquery-3.1.1.min.js",
  "js/jquery-ui.min.js",
  "js/primeelements.min.js",
  "js/primeui-all.min.js",
  "js/main.js"
]
}

I have the required JavaScript libraries inside "js" folder in the root directory, for security reasons I do not retrieve them from a CDN.

My HTML contains CSS and JS on <head> section, but the styles nor the scripts applies in any way.

<html>
<head>
    <link rel="stylesheet" href="css/font-awesome.min.css"/>
    <link rel="stylesheet" href="css/jquery-ui.min.css"/>
    <link rel="stylesheet" href="css/primeui-all.min.css"/>

    <script type="text/javascript" src="js/jquery-3.1.1.min.js"></script>
    <script type="text/javascript" src="js/jquery-ui.min.js"></script>
    <script type="text/javascript" src="js/primeui-all.min.js"></script>
    <script type="text/javascript" src="js/x-tag-core.min.js"></script>
    <script type="text/javascript" src="js/primeelements.min.js"></script>
</head>
<body>
    <button id="btn-show" type="button">Show</button>
    <div id="dlg" title="Godfather I">
        <p>The story begins as Don Vito Corleone, ...</p>
    </div>

</body>
</html>
// main.js
$(function() {
$('#dlg').puidialog({
    showEffect: 'fade',
    hideEffect: 'fade',
    minimizable: true,
    maximizable: true,
    responsive: true,
    minWidth: 200,
    modal: true,
    buttons: [{
        text: 'Yes',
        icon: 'fa-check',
        click: function() {
            $('#dlg').puidialog('hide');
        }
    },
        {
            text: 'No',
            icon: 'fa-close',
            click: function() {
                $('#dlg').puidialog('hide');
            }
        }
    ]
});
$('#btn-show').puibutton({
    icon: 'fa-external-link-square',
    click: function() {
        $('#dlg').puidialog('show');
    }
});
}

The button is successfully displayed but with no events are attached nor styles.

i.e: if I inject this code on StackOverflow my button get the styles from SO and not mine.

I want to avoid as much as possible JavaScript for creating elements, or injecting tags programmatically, to separate the View from the Controller.

How can I manage to use my styles and scripts with my injected HTML as we do ordinarily? I have tried adding them to web_accessible_resources, and other manifest fields unsuccessfully.

1

There are 1 answers

1
another On BEST ANSWER

Makyen explained:

It is not possible to parse HTML with <head> elements hardcoded such <script> and <link>. Instead it should be injected in other way.

The ways I have chosen to inject the files are:

For the CSS:

// Test.js

/** CSS Injection */
var link = document.createElement("link");
link.href = chrome.extension.getURL("css/jquery-ui.min.css");
link.type = "text/css";
link.rel = "stylesheet";
document.getElementsByTagName("head")[0].appendChild(link);

For the JS:

// Test.js

/** JavaScript Injection */
var s = document.createElement('script');
s.src = chrome.extension.getURL('js/jquery-3.1.1.min.js');
// "When an inline script is inserted in the document, it's immediately executed 
// and the <script> tag can safely be removed". – Rob W.
s.onload = s.remove;
(document.head || document.documentElement).appendChild(s);

Trying to insert large amounts of code/libraries as into the page context using tags has a significant chance of interfering with the current contents/JavaScript of the page. Is there a reason that you are inserting scripts into the page context? Normally, you would have these inserted scripts (and CSS) as content scripts (manifest.json content_scripts, or chrome.tabs.executeScript()/chrome.tabs.insertCSS(). not in the page context. – Makyen

With the above code I have successfully injected the files, but the styles and functions are applied only sometimes, I will edit when I fix this.