1

I've created a Chrome Extension based on https://thoughtbot.com/blog/how-to-make-a-chrome-extension (look at the end for the completed files)

I've made two changes: I used jQuery 3.1.1 minimized instead of the older one given in the page above, and I changed content.js:

chrome.runtime.onMessage.addListener(
    function(request, sender, sendResponse) {
        if( request.message === "clicked_browser_action" ) {
            $(document).off("touchmove mousewheel mousemove scroll");
        }
    }
);

The $(document).off() command is not working when I click the extension's button with an appropriate test page opened. It doesn't give an error, it just doesn't do anything.

I've checked. If I type $(document).off("touchmove mousewheel mousemove scroll"); into the console on the page I want to affect, it works fine. If run in the extension it isn't.

I tried checking that document was correct in the extension, and document.domain gives the correct result when checked in a breakpoint in the extension (and in the console on the page).

I tried checking jQuery._data( jQuery(document)[0]).events["touchmove"] (and "mousewheel", "mousemove", "scroll") and it works in the console, but I get an error (Cannot read property 'touchmove' of undefined) if I breakpoint the extension. When I checked further, jQuery._data( jQuery(document)[0]) gives the following:

In console:

Object
    events: Object
    handle: function (e)
    __proto__: Object

In breakpoint:

Object
    __proto__: Object

I've tried a few other things (for example, that jQuery is accessible and working, and so on).

Makyen
  • 31,849
  • 12
  • 86
  • 121
James Carlyle-Clarke
  • 830
  • 1
  • 12
  • 19
  • Are you trying to turn off event listeners you added in your content script, or that already exist in the page script (i.e. are part of the webpage)? It sounds like the events you are trying to turn off are in the page script. – Makyen Oct 29 '16 at 15:40
  • I'm trying to turn off event listeners that already exist in the page script (i.e. are part of the webpage). – James Carlyle-Clarke Oct 29 '16 at 15:50

1 Answers1

2

You're attempting to execute $(document).off() in order to remove jQuery event handlers that were added by page scripts (scripts that already exist in the webpage; i.e. not your content script). jQuery stores which event handlers have been added in data structures internal to the jQuery instance being used. Your content script is in a different context/scope from that of page scripts. Thus, when you execute $(document).off() in the context of your content script, that jQuery instance isn't aware of any of the even handlers which have been added by the page scripts and can't remove them.

For your $(document).off() to be effective, it must be run in the page script context. The most common way to have code execute in the page script context is to create and insert a <script> element to the page's DOM.

You could change your code to:

chrome.runtime.onMessage.addListener(
    function(request, sender, sendResponse) {
        if( request.message === "clicked_browser_action" ) {
            let newScript = document.createElement('script');
            newScript.innerHTML='$(document).off("touchmove mousewheel mousemove scroll");';
            document.head.appendChild(newScript);
        }
    }
);

The added script gets run in the page context because it's now a <script> element in the DOM. The browser recognizes that a <script> element was added and evaluates it (executes the code contained) as soon as the script which inserted it is no longer processing. It does basically the same thing for any other element you add to the DOM. Because it is part of the page, the code inside the <script> gets run in the page script context/scope.

While the above works, particularly for such short code, my preferred method of creating <script> elements to execute code in the page context is to use a function I wrote called executeInPage(). You can find that function in my answer to "Calling webpage JavaScript methods from browser extension".

Makyen
  • 31,849
  • 12
  • 86
  • 121
  • Amazing! That certainly seems to work, and I will mark it as the answer shortly (though you might want to edit the quotation marks in the $(document) line). You can colour me confused as to how the inserted script actually gets run, though (it does, but WHY?)... can you clarify? – James Carlyle-Clarke Oct 29 '16 at 16:20
  • 1
    Fixed. I've been doing too much markdown editing. It gets run because it is a ` – Makyen Oct 29 '16 at 16:32
  • You're awesome Makyen, thanks so much, that's been driving me crazy. I have to say that I suspected it was going to be a scope thing but had no idea of the solution, and the advantage is I've just learned something about appending script too... – James Carlyle-Clarke Oct 29 '16 at 16:38
  • 1
    I'm glad I was able to help. Note that in order to be executed, ` – Makyen Oct 29 '16 at 16:44
  • 1
    Just FYI: That blog page (& many examples) shows something that is not really a good practice. If your content script is only initiated from a `browser_action` button, it is better to use `tabs.executeScript()` to only inject the content script at the time the script is needed (when the `browser_action` button is pressed) rather than using a `content_script` key in *manifest.json* which injects into ``. Basically, your content script uses resources in every page it is injected. If it does not need to be injected prior to the `browser_action` click, it probably should not be. – Makyen Oct 29 '16 at 16:56
  • Huh. So from a quick scan it seems like I run `tabs.executeScript()` from background.js, inside `chrome.browserAction.onClicked.addListener(function(tab) {}`, and need the activeTab permission. Is that right? (Apologies for asking what may be obvious, it's getting really late!) – James Carlyle-Clarke Oct 29 '16 at 17:06
  • 1
    Yes, that is correct.You can then either inject code, or a script file. Injecting with `tabs.executeScript()` will also negate the need to be listening in the content script for a message telling it the `browser_action` button was clicked. If it is running, then the button was clicked. Note also that blog injects jQuery to perform a single line of code (easily written as a single line of generic JavaScript). That is particularly wasteful to have jQuery also injected in ``. jQuery is good, but heavy handed to shorten 1 line of code run in 1 browser (i.e. using 90+KB to save 20 bytes). – Makyen Oct 29 '16 at 17:39