1

I'm working on a chrome extension that uses the Bootstrap Collapse (v2.3.2) functionality on dynamically created elements by attaching event listeners at render time. I'm running into a problem where usage of collapse on the parent page no longer works when I load my extension. I tested this on bootstrap's documentation and was able to reproduce. The collapse stops functioning in the example while my extension functions correctly. I figure there must be some pollution of the global jQuery object when I load my version of bootstrap.js.

This is the script portion of my manifest:

"content_scripts": [{
  "run_at": "document_start",
  "matches": ["<all_urls>"],
  "js": [
    "js/vendor/jquery-min.js",
    "js/vendor/bootstrap.min.js",
    "js/content_script.js",
  ]
}]

And inside js/content_script.js

// #render
function render () {
  $elInIframe.find('.accordion-toggle').on('click', function() {
      var targetName = $(this).attr('data-target');
      var target = $elInIframe.find(targetName);
      target.collapse('toggle');
  });
}

The lifecycle of my extension is:

  1. Load content_script (uses jquery and bootstrap loaded by extension as content_scripts).
  2. Initialize and inject iframe.
  3. Get data asyncronously and inject as HTML inside the iframe
  4. call render, attaching click listeners to the element in the iframe

So to clarify, my extension works as expected, but I'm breaking pages that also utilize bootstrap.js.

As a possible solution I tried moving bootstrap.js to a web_accessible_resource and loading it in the template. However, after doing so, calling target.collapse results in an error (not a function) as the jquery used by my extension isn't modified by loading bootstrap.js -- and I'm not even sure bootstrap is loading correctly in the iframe by the extension.

sirugh
  • 324
  • 2
  • 11
  • I think it will work if you [inject a ` – wOxxOm Nov 11 '15 at 10:54

1 Answers1

1

My solution was very roundabout. I ended up creating a script that could be injected at runtime and exposed it as a web_accessible_script.

//manifest.json
"web_accessible_resources": [
  "js/bootstrap.min.js",
  "js/injected_script.js"
]


//injected_script.js
document.addEventListener('toggle-collapse', function (e) {
  var targetName = e.detail.targetName;
  var $iframe = $('#extension_iframe');
  var target = $iframe.contents().find(targetName)
  target.collapse('toggle')
}, false);

Bootstrap and the injected script are appended at runtime via jquery:

var injectedLink = chrome.extension.getURL('js/injected_script.js');
var bootstrapLink = chrome.extension.getURL('js/bootstrap.min.js');

var $iframeHead = iframe.contents().find('html').find('head')
injectScript($iframeHead, injectedLink);
injectScript($iframeHead, bootstrapLink);

function injectScript ($parent, url) {
    var s = document.createElement("script");
    s.type = "text/javascript";
    s.src = url;
    $parent.append(s);
}

I then replaced the call to .collapse('toggle') with a custom event that could be 'thrown over the wall' to my injected script.

//content_script.js
document.dispatchEvent(new CustomEvent('toggle-collapse', {
    detail: {
        targetName: targetName
    }
}));

In this way I was able to load bootstrap for the iframe in my extension and be able to communicate with the jquery that my extension was using. All without polluting the DOM.

sirugh
  • 324
  • 2
  • 11