Search code examples
google-chrome-extensionbrowser-pluginprime-ui

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


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.


Solution

  • 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.