5

I am making an extension for Chrome browser that uses contextMenus to change CSS of the selected text.

But I am not able to access the HTML structure i.e. parentNode of the selected text as I can do very easily in this example.

var selection = window.getSelection();

If used by default in browser, this returns parentNode of the selected text that I can use to change the CSS later.

How to implement this using a Chrome Browser Extension?

Sahil
  • 315
  • 5
  • 21

1 Answers1

6

Since that Chrome doesn't let you interact with the element you have clicked on using the context menu, you have to create a content script that stores the last element that has been right-clicked on the page, so when the user right-clicks any element, you'll be able to use it.

First you have to create a save_last_element.js content script, like this:

var LAST_SELECTION,
    LAST_ELEMENT;

document.body.addEventListener('contextmenu', function(e) {
    LAST_SELECTION = window.getSelection();
    LAST_ELEMENT = e.target;
    // this will update your last element every time you right click on some element in the page
}, false);

Then you'll add it in your manifest.json:

"permissions": ["*://*/*"],
"content_scripts": [
    {
        "matches": ["*://*/*"],
        "js": ["/path/to/save_last_element.js"],
        "run_at": "document_idle",
        "all_frames": true
    }
]

Now, when injecting a script in the page, you'll be able to use the LAST_SELECTION and LAST_ELEMENT variables to refer to the last right-clicked element and edit its CSSs or whatever you want.

In your background.js you should do something like this:

function handler(info, tab) {
    // here you can inject a script inside the page to do what you want
    chrome.tabs.executeScript(tab.id, {file: '/path/to/script.js', allFrames: true});
}

chrome.runtime.onInstalled.addListener(function() {
    chrome.contextMenus.create({
        "title": "Some title",
        "contexts": ["all"],
        "documentUrlPatterns": ["*://*/*"],
        "onclick": handler
    });
});

Note that the context menu is being registered inside a chrome.runtime.onInstalled listener, since context menus registrations are persistent and only need to be done when installing the extension.

And finally, inside your script.js file:

if (LAST_SELECTION) {
    // do whatever you want with the information contained in the selection object
}
if (LAST_ELEMENT) {
    // do whatever you want with the element that has been right-clicked
}
Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
  • You can also use messaging to pass data between the injected script and the background script. https://developer.chrome.com/extensions/messaging – oyvind Oct 04 '14 at 01:25
  • That's slower. When the user clicks the context menu item you can inject the script without sending any message and it will work fine. – Marco Bonelli Oct 04 '14 at 06:57
  • Yes, but if you want to pass additional parameters, you probably want messaging, or else you would need to have a separate script for each case, wouldn't you? – oyvind Oct 04 '14 at 12:10
  • Messaging is only another way of doing this kind of stuff, you can choose the method you like most. The OP's request can be solved more easily without messaging. – Marco Bonelli Oct 04 '14 at 12:41
  • Isn't there a little bit simpler way to do this? I'm unable to follow this up completely.. – Sahil Oct 23 '14 at 21:00
  • 1
    there's no "simple" way to do this.. it's one of the weak points of google extensions – Marco Bonelli Oct 23 '14 at 21:28
  • if you get error: `Error in event handler for contextMenus: Error: Invalid value for argument 2. Property 'all_frames': Unexpected property.` it is because the property must be named: `allFrames` – Fed Nov 22 '18 at 14:50