Replace each word in the paragraphs of the web page with the button containing this text

I am working on a Google Chrome extension that accepts all paragraphs (the contents of the p tag) and puts each word in them in one button. This is part of a larger program I'm working on. I have a working copy of this part of the application on JSFiddle .

Now I am trying to port this code to the Chrome extension. But I am having trouble accessing the DOM from my background script, so that I can manipulate it with my code (in my FormatText() function). I haven't named the function yet because I can't figure out how I should edit the DOM in the first place in background.js.

Here is my code:

manifest.json

 { "manifest_version": 2, "name": "Test Extension", "version": "1", "background": { "persistent": false, "scripts": ["background.js","jquery-3.0.0.min.js","TextSplitter.js"] }, "content_scripts": [{ "matches": ["<all_urls>"], "js": ["content.js"] }], "browser_action": { "default_title": "Test Extension" }, "permissions": ["activeTab","tabs"] } 

content.js

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

background.js

 // A function to use as callback function doStuffWithDom(domContent) { console.log('I received the following DOM content:\n'); console.log(JSON.stringify(domContent)); var domAccess = $(domContent); var myText = $(domAccess).find("p").text(); console.log("THIS IS MY TEXT: " + myText); } chrome.tabs.onUpdated.addListener(function (tabID, info, tab) { console.log("Status: " + info.status); if (info.status == "complete") { chrome.tabs.sendMessage(tab.id, { text: 'report_back' }, doStuffWithDom); } }); 

TextSplitter.js

 function FormatText(domObject) { var pElements = []; // Holds the split paragraphs for each p tag var pElementIndex = 0; //Loop through each p tag in web page $("p").each(function (webPElementIndex, webPElement) { var jQObjWebPElement = $(webPElement);// Convert p element to jQuery Obj // split current paragraph element text into an array of seperate words pElements[webPElementIndex] = jQObjWebPElement.text().split(" "); }); //Clear text out of all p elements $("p").empty(); //Loop through p elements in the webpage to add back text with spans around each word $("p").each(function (webPElementIndex, webPElement) { // convert current web page element to jQuery Obj var jQObjWebPElement = $(webPElement); // Loop through each word stored in each stored paragraph element $.each(pElements[pElementIndex], function (pElementWordIndex, pElementWord) { var jQObjPElementWord = $(pElementWord); // convert element to jQuery object jQObjWebPElement.append($("<button>") .text(pElements[pElementIndex][pElementWordIndex])); }); pElementIndex = pElementIndex + 1; }); } 

Please forgive my ignorance, as I am very new to working with the DOM in general, especially in the Chrome extension.

0
source share
1 answer

Your code looks too complex in several areas. In particular, the DOM can only be manipulated from within the contents of the script. As mentioned in a wOxxOm comment, it would be nice to read a review of the Chrome extension architecture . It has general architecture information that should help you understand how things are done / organized.

The following full extension (tested in Chrome and Firefox) changes all non-space characters surrounded by a space character (or the beginning or end of a line / paragraph) within <button> . This is done by clicking actionButton in the browser user interface.

When the actionButton button is actionButton contentScript.js file is inserted. The content of the script makes the changes and exits. For this function, there is no need for a content script that sits on the page, waiting for a message to be received to perform a simple function. You can really do more than you described / shown with the code, but for the functions mentioned in the Question, inserting a script using tabs.executeScript() is a better, less complex, and more efficient choice.

I decided not to use jQuery. jQuery is good for many things. In this case, I didn’t like the compromise of loading 90 KiB code to save just a few characters instead of doing the exact same thing with JavaScript.

I was not very well versed in the code that you use to execute your button. I already had the code in another answer that can be easily adapted to accomplish this task. Given that your question was about how to manipulate the DOM, and not about the functionality of your button code, I decided to use code that I already know.

Extension in action:

button-izing example.com

manifest.json

 { "description": "Inject content script to make all words in <p> elements buttons", "manifest_version": 2, "name": "Button all words in <p>", "version": "0.1", "permissions": [ "activeTab" ], "background": { "scripts": [ "background.js" ] }, "browser_action": { "default_icon": { "32": "myIcon.png" }, "default_title": "Make Buttons" } } 

background.js:

 chrome.browserAction.onClicked.addListener(function(tab) { //Inject the script to change the text in <p> to buttons chrome.tabs.executeScript(tab.id,{file: 'contentScript.js'}); }); 

contentScript.js:

 (function(){ function handleTextNode(textNode) { if(textNode.nodeName !== '#text' || textNode.parentNode.nodeName === 'SCRIPT' || textNode.parentNode.nodeName === 'STYLE' ) { //Don't do anything except on text nodes, which are not children // of <script> or <style>. return; } let origText = textNode.textContent; let newHtml=origText.replace(/(^|\s)(\S+)(?=\s|$)/mg, '$1<button>$2</button>'); //Only change the DOM if we actually made a replacement in the text. //Compare the strings, as it should be faster than a second RegExp operation and // lets us use the RegExp in only one place for maintainability. if( newHtml !== origText) { let newSpan = document.createElement('span'); newSpan.innerHTML = newHtml; textNode.parentNode.replaceChild(newSpan,textNode); } } //Find all text node descendants of <p> elements: let allP = document.querySelectorAll('p'); // Get all <p> let textNodes = []; for (let p of allP) { //Create a NodeIterator to get the text nodes descendants of each <p> let nodeIter = document.createNodeIterator(p,NodeFilter.SHOW_TEXT); let currentNode; //Add text nodes found to list of text nodes to process below. while(currentNode = nodeIter.nextNode()) { textNodes.push(currentNode); } } //Process each text node textNodes.forEach(function(el){ handleTextNode(el); }); })(); 

myIcon.png:

Icojam-Weathy-24-tornado.png

The code in handleTextNode , to make changes to the text nodes, was changed from the code of my other answer .

+3
source

Source: https://habr.com/ru/post/1259668/


All Articles