18

I'm trying to access some DOM elements from a webpage:

<html> <button id="mybutton">click me</button> </html> 

I want to access the innerHTML ("click me") through a chrome extension:

chrome.browserAction.onClicked.addListener(function(tab) { var button = document.getElementById("mybutton"); if(button == null){ alert("null!"); } else{ alert("found!"); } }); 

When I click the extension, the popup says: "null". My manifest.json:

{ "name": "HackExtension", "description": "Hack all the things", "version": "2.0", "permissions": [ "tabs", "http://*/*" ], "background": { "scripts": ["contentscript.js"], "persistent": false }, "browser_action": { "scripts": ["contentscript.js"], "persistent": false }, "manifest_version": 2 } 
1

2 Answers 2

36

The solution: You need a manifest file, a background script and a content script. This is not really clear in the documentation that you have to use it and also, how to use it. For alerting the full dom, see here. Because I have a hard time finding a complete solution that actually works and not just snippets that are useless for newbies, like me, I included a specific solution:

manifest.json

{ "manifest_version": 2, "name": "Test Extension", "version": "0.0", "background": { "persistent": false, "scripts": ["background.js"] }, "content_scripts": [{ "matches": ["file:///*"], "js": ["content.js"] }], "browser_action": { "default_title": "Test Extension" }, "permissions": ["activeTab"] } 

content.js

/* Listen for messages */ chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) { /* If the received message has the expected format... */ if (msg.text && (msg.text == "report_back")) { /* Call the specified callback, passing the web-pages DOM content as argument */ sendResponse(document.getElementById("mybutton").innerHTML); } }); 

background.js

/* Regex-pattern to check URLs against. It matches URLs like: http[s]://[...]stackoverflow.com[...] */ var urlRegex = /^file:\/\/\/:?/; /* A function creator for callbacks */ function doStuffWithDOM(element) { alert("I received the following DOM content:\n" + element); } /* When the browser-action button is clicked... */ chrome.browserAction.onClicked.addListener(function(tab) { /*...check the URL of the active tab against our pattern and... */ if (urlRegex.test(tab.url)) { /* ...if it matches, send a message specifying a callback too */ chrome.tabs.sendMessage(tab.id, { text: "report_back" }, doStuffWithDOM); } }); 

index.html

<html> <button id="mybutton">click me</button> </html> 

Just save the index.html somewhere and load in the folder as an extension, containing the three other files. Open the index.html and push the extension button. It should show "click me".

Sign up to request clarification or add additional context in comments.

5 Comments

To add to this: The content script is the script that can access the web page's DOM. It can pass elements to another script if you need to. The example above uses a background script, but it could be a popup.
do you mind explaining the "matches": ["file:///*"], line? Isn't this just for local files?
It works, but it stop working as soon as I add a "default_popup": "popup.html" in "browser_action" (manifest.json)
Is there a way to listen to DOM elements after the extension has been installed? i mean without the onclick event listener?i tried it inside chrome.runtime.onInstalled.addListener but didn't work or i didn't do it correctly.
It returns undefined.....
0

Starting with Manifest V3, your content scripts won't be able to access anything generated by other loaded scripts and using a trick like inlining a your code inside <script> tag won't work due to stricter CSP rules. This caused me a lot of head ache since I couldn't figure out how to access library-generated DOM properties similar to React or Redux DevTools.

Instead, you have to now inject your script inside the service_worker with eg:

chrome.scripting.registerContentScripts([ { id: 'inject', matches: ['<all_urls>'], js: ['inject.js'], runAt: 'document_end', world: 'MAIN' } ]) 

Notice the 'MAIN' property, not the default 'ISOLATED'. Then inside my inject.js I do whatever, eg:

window.addEventListener('load', () => { findReact() }) 

Also you have to add the script to the manifest.json:

 "web_accessible_resources": [ { "resources": ["inject.js"], "matches": ["<all_urls>"], "extension_ids": [] } ], "externally_connectable": { "ids": ["*"] }, 

Not sure is "externally_connectable" needed. And you need to add at least "scripting" permissions. I used the React DevTools migration as my source https://github.com/facebook/react/pull/25145

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.