Detect and handle a button click in the active HTML page
Asked Answered
P

1

0

Currently, I am creating a WebExtension for Firefox which displays a small div in the active HTML page on the active tab after 6 seconds (not the popup.html in browser_action!). I made a small example that shows a button in the div. My problem is that I don't know how to detect a click event of this button so I can kick off a function? In the moment I handle the event listener in content.js

manifest.json:

{
  "manifest_version": 2,
  "name": "Study06",
  "version": "1.0",
  "description": "My web extension",
  "permissions": [
  "tabs",
    "<all_urls>",
    "alarms"
  ],
  "background": {
    "scripts": ["background.js"]
  }
}

background.js:

browser.alarms.create("", {delayInMinutes: 0.1});
browser.alarms.onAlarm.addListener(injectScript);
function getActiveTab() {
  return browser.tabs.query({active: true, currentWindow: true});
}
function injectScript() {
  getActiveTab().then((tabs) => {
    browser.tabs.executeScript(null, {
      file: "/content.js"
    });
    // send message to contentscript
    browser.tabs.sendMessage(tabs[0].id, {message: "Hello"});
  });
}

After 6 seconds, content.js will be loaded and a message to content.js will be sent.

content.js:

document.addEventListener('DOMContentLoaded', function() {
  var button = document.getElementById('button_id');
  button.addEventListener('click', function() {
    console.log("CLICK");
  });
});

browser.runtime.onMessage.addListener(createModal);

function createModal(){
  document.getElementsByTagName('body')[0].appendChild(modal1);
}

//create div with button
var modal1 = document.createElement("div");
modal1.style.paddingTop = "100px";
modal1.setAttribute("id", "mymodal1");
modal1.style.position = "fixed";
modal1.style.top = 0;
modal1.style.left = 0;
modal1.style.zIndex = 1000000;
modal1.style.overflow = "auto";
var button1 = document.createElement("button");
button1.setAttribute("id", "button_id");
button1.innerHTML = "Click me";
modal1.appendChild(button1);

The event listener in content.js is not working. I also created a JavaScript file javascriptForButtonEvent.js with an event listener for the button and added this script in manifest content_script. Also, to inject this file in background script like:

browser.tabs.executeScript(null, {
      file: "/content.js";
      file: "/javascriptForButtonEvent.js"
    });

But, these solutions didn't work for me (when clicking the button nothing happens). Is there a way to handle events from the active HTML page?

Puerility answered 2/3, 2017 at 10:55 Comment(0)
B
3

Your click event listener never gets added to the DOM. You have:

document.addEventListener('DOMContentLoaded', function() {

When injecting scripts, unless you are explicitly injecting at document_start, your script is guaranteed to be injected at least after DOMContentLoaded has already fired.1 Given that the event has already fired, and only fires once, your DOMContentLoaded listener is never called. As a result of how injection works, you do not need to listen for the DOMContentLoaded event at all. Just move your initialization such that it executes in the main sequence of your code (after you add the button):

document.getElementById('button_id').addEventListener('click', function() {
    console.log("CLICK");
});

No need to search for the <button> you create

However, in this case, you are adding this button yourself. There is no need to go searching for it in the DOM. You already have a reference to it. Add the listener at the time you are creating the button:

var button1 = document.createElement('button');
button1.id = 'button_id';
button1.textContent = 'Click me';
button1.addEventListener('click', function() {
    console.log("CLICK");
});
modal1.appendChild(button1);

Don't use generic IDs or classes

For extensions, you should not use IDs or classes that are as generic as button_id. Doing so results in a good chance that you will have a name collision with the random pages into which you are injecting content. You should pick a sequence of text which you use as a "namespace". You can then prepend this namespace to all IDs and classes. For instance, your ID should be something like: Study06-button_id. Exactly what you use is a personal choice. But, it should be specific to the extension you are working on.

When listening for DOMContentLoaded, you should check readyState

Any time you use a DOMContentLoaded listener, or a window load listener, you should always check the document.readyState to make sure that you are adding the listener prior to the DOMContentLoaded event being fired (or load event, if that is what you are listening for). This should be normal habit when you want to listen for these events. If you add the listener after the event has fired, the listener will never be run.

For adding a DOMContentLoaded listener, you should use something like:

if(document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded',afterLoaded);
} else {
    afterLoaded();
}

function afterLoaded(){
    //Everything that needs to happen after the DOM has initially loaded.
}

  1. Note that when using tabs.executeScript() you have to seriously work at having your tabs.executeScript() execute in a time-frame where the script is actually injected at document_start. The exact timing that is needed is different for different browsers. If you use a manifest.json content_script entry, this timing is handled by the browser.
Brynnbrynna answered 2/3, 2017 at 15:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.