用包含该文字的按钮替换网页段落中的每个单词

时间:2016-11-11 06:25:06

标签: javascript jquery html dom google-chrome-extension

我正在开发一个Google Chrome扩展程序,它会记录所有段落(p标记内容),并将每个单词放在一个按钮中。这是我正在开展的一个更大的计划的一部分。我在JSFiddle上有应用程序该部分的工作副本。

现在,我正在尝试将该代码移植到Chrome扩展程序中。但是,我无法从后台脚本访问DOM,因此我可以使用我的代码(在我的函数FormatText()中)操作它。我还没有调用该函数,因为我无法弄清楚我应该如何在 background.js 中首先编辑DOM。

这是我的代码:

的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's 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;
    });
}

请原谅我的无知,因为我一般都不熟悉DOM,特别是在Chrome扩展程序中。

1 个答案:

答案 0 :(得分:3)

您的代码在某些方面显得过于复杂。特别是,DOM只能从内容脚本中操作。正如评论中提到的wOxxOm,您最好阅读Chrome extension architecture overview。它具有整体架构信息,可以帮助您了解事情通常如何完成/组织。

以下完整扩展程序(在Chrome和Firefox上测试)会将由空白字符(或行/段落的开头或结尾)包围的所有非空白字符更改为<button>。当在浏览器用户界面中单击actionButton时,它会执行此操作。

单击actionButton时,会注入 contentScript.js 文件。内容脚本进行更改和退出。对于此功能,不需要位于页面中的内容脚本等待获取消息以执行简单功能。实际上,您可能做的事情比您用代码描述/显示的要多,但是对于问题中提到的功能,使用tabs.executeScript()注入脚本是一种更好,更简单,更有效的选择。

我选择不使用jQuery。 jQuery很适合很多东西。在这种情况下,我不喜欢加载90 KiB代码以节省几个字符而不是用库存JavaScript完成同样的事情。

我没有仔细查看您用于执行按钮化的代码。我已经在another answer中有了代码,可以很容易地适应这项任务。鉴于您的问题是关于如何操作DOM,而不是关于按钮化代码的功能,我选择使用我已经熟悉的代码。

行动中的扩展:

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

handleTextNode中用于修改文本节点的代码已从another answer of mine中的代码修改。