Javascript 使用 Chrome 扩展更改 DOM 内容

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34467627/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 16:23:15  来源:igfitidea点击:

Change DOM Content With Chrome Extension

javascriptjquerygoogle-chromegoogle-chrome-extension

提问by wuno

I am building a Chrome extension. I am trying to get my app to communicate with each page in the extension and the page the user is viewing in the browser. I need to access the dom from the extension and then update it.

我正在构建一个 Chrome 扩展程序。我试图让我的应用程序与扩展程序中的每个页面以及用户在浏览器中查看的页面进行通信。我需要从扩展访问 dom,然后更新它。

manifest.json 
popup.html
popup.js
background.js 
content.js

and the current page the user is viewing.

以及用户正在查看的当前页面。

My goal is on page load modify the dom and show the user the new version of the page before they ever see it. in popup.jsusers are allowed to enter keywords into the popup. The keywords are saved to localStorageand while they view the web the keywords are censored out of their view by making the parent div of the keywords hidden if it is found on any pages they are viewing.

我的目标是在页面加载时修改 dom 并在用户看到它之前向用户显示页面的新版本。在popup.js允许用户输入关键字到弹出。关键字被保存到localStorage,当他们查看网络时,如果在他们正在查看的任何页面上找到关键字的父 div,他们就会隐藏关键字的父 div,从而将关键字从他们的视图中删除。

I need help getting each page to communicate and I think the way I am hiding the parent divs in popup.js won't work. I am confused on how to perform the action on the dom from the front.

我需要帮助让每个页面进行通信,我认为我在 popup.js 中隐藏父 div 的方式行不通。我对如何从前面对 dom 执行操作感到困惑。

Send the dom to the background.js Find keywords on the page and change their parent divs to hidden. push the dom back to the viewing page.

将dom发送到background.js,在页面上查找关键字,将其父div改为隐藏。将 dom 推回查看页面。

I think this line is saying if I match any url then run my app but I am not sure.

我认为这一行是说如果我匹配任何 url 然后运行我的应用程序,但我不确定。

  "matches":    ["*://*/*"],

My manifest.json

我的 manifest.json

{
 "name": "Wuno Zensoring",
  "version" : "1.0",
   "permissions": [
   "activeTab",
   "tabs",
   "storage"
   ],
  "description": "This extension will search the document file for keywords and hide their parent div.",
  "icons": {                   
    "19": "icon19.png",
    "38": "icon38.png",
    "48": "icon48.png",
    "128": "icon128.png"  
  },    
    "background": {
    "persistent": false,
    "scripts": ["jquery-1.11.3.min.js","background.js"]
  },
     "content_scripts": [{
        "matches":    ["*://*/*"],
        "js":         ["content.js"],
        "run_at": "document_end",
        "all_frames": true
    }],
     "web_accessible_resources": [
        "popup.js", "content.js"
        ],
  "browser_action": {
    "default_icon": "icon.png128",
    "default_popup": "popup.html",
    "default_icon": {                   
      "19": "icon19.png",
      "38": "icon38.png",
      "48": "icon48.png",
      "128": "icon128.png"        
  }
  },
     "manifest_version": 2
}

popup.html

弹出窗口.html

<!doctype html>
<html>
  <head>
    <title>Wuno Zensorship</title>
    <script src="jquery-1.11.3.min.js"></script>
        <script src="popup.js"></script>
    <link rel="stylesheet" type="text/css" href="styles.css">
  </head>
  <body>
    <img src="icon48.png">

 <section>
<form id="form" action="#" method="POST">
<input id="description" name="description" type="text" />
<input id="add" type="submit" value="Add" />
<button id="clearChecked">Clear Checked Items</button>
<button id="clear">Clear All</button>
</form>
<div id="alert"></div>
<ul id="keyWords"></ul>
</body>
</html>

popup.js

弹出窗口.js

   $(document).ready(function () {
localArray = [];

if (!localStorage.keyWords) {
  localStorage.setItem('keyWords', JSON.stringify(localArray));
}

loadKeyWords();

function loadKeyWords() {
    $('#keyWords').html('');
    localArray = JSON.parse(localStorage.getItem('keyWords'));
    for(var i = 0; i < localArray.length; i++) {
      $('#keyWords').prepend('<li><input class="check" name="check" type="checkbox">'+localArray[i]+'</li>'); 
        }
    }

$('#add').click( function() {
   var Description = $('#description').val();
  if($("#description").val() === '') {
    $('#alert').html("<strong>Warning!</strong> You left the to-do empty");
    $('#alert').fadeIn().delay(1000).fadeOut();
    return false;
   }
   $('#form')[0].reset();
   var keyWords = $('#keyWords').html();
   localArray.push(Description);
   localStorage.setItem('keyWords', JSON.stringify(localArray));
   loadKeyWords();
   return false;
});

$('#clear').click( function() {
window.localStorage.clear();
location.reload();
return false;
});

$('#clearChecked').click(function() {
  currentArray = [];
  $('.check').each(function() {
    var $curr = $(this);
    if (!$curr.is(':checked')) {
      var value = $curr.parent().text();
      currentArray.push(value);
      localStorage.setItem('keyWords', JSON.stringify(currentArray));
      loadKeyWords();
    } else {
      $curr.parent().remove();
    }
  });
});


// Update the relevant fields with the new data
function setDOMInfo(info) {
  $("div p:contains(localStorage.getItem('keyWords')).parent('div').hide()");
}

// Once the DOM is ready...
window.addEventListener('DOMContentLoaded', function () {
  // ...query for the active tab...
  chrome.tabs.query({
    active: true,
    currentWindow: true
  }, function (tabs) {
    // ...and send a request for the DOM info...
    chrome.tabs.sendMessage(
        tabs[0].id,
        {from: 'popup', subject: 'DOMInfo'},
        // ...also specifying a callback to be called 
        //    from the receiving end (content script)
        setDOMInfo);
  });
});


}); // End of document ready function

background.js

背景.js

chrome.runtime.onMessage.addListener(function (msg, sender) {
  // First, validate the message's structure
  if ((msg.from === 'content') && (msg.subject === 'showPageAction')) {
    // Enable the page-action for the requesting tab
    chrome.pageAction.show(sender.tab.id);
  }
});

content.js

内容.js

// Inform the background page that 
// this tab should have a page-action
chrome.runtime.sendMessage({
  from:    'content',
  subject: 'showPageAction'
});

// Listen for messages from the popup
chrome.runtime.onMessage.addListener(function (msg, sender, response) {
  // First, validate the message's structure
  if ((msg.from === 'popup') && (msg.subject === 'DOMInfo')) {
    // Collect the necessary data 
    // (For your specific requirements `document.querySelectorAll(...)`
    //  should be equivalent to jquery's `$(...)`)
    var domInfo = {
      total:   document.querySelectorAll('*').length,
      inputs:  document.querySelectorAll('input').length,
      buttons: document.querySelectorAll('button').length
    };

    // Directly respond to the sender (popup), 
    // through the specified callback */
    response(domInfo);
  }
});

采纳答案by deep

You need to use this query to send data to DOM from the current tab being viewed.

您需要使用此查询将数据从当前正在查看的选项卡发送到 DOM。

    chrome.tabs.executeScript(null, {
        code: 'var config = ' + JSON.stringify(getKeywords)
    }, function() {
        chrome.tabs.executeScript(null, {file: 'custom.js'});
    });

and in the custom.jsfile you can write you function that you want to apply on the DOM element. like if you want to hide something than you need that query in custom.js. So if you wanted to use that example you would need to modify it according to your requirements.

custom.js文件中,您可以编写要应用于 DOM 元素的函数。就像如果你想隐藏一些东西而不是你需要在custom.js. 因此,如果您想使用该示例,则需要根据您的要求对其进行修改。

var all = document.getElementsByTagName("div");
var searchValue=config.toString().split(',');
alert('Example:' + searchValue[0]);
for(j=0; j < searchValue.length; j++) {
for(i=0; i < all.length; i++) {
    if(all[i].innerHTML.indexOf(searchValue[j]) > -1){
    all[i].innerHTML = ""
    }
}
}

回答by Haibara Ai

You should send command from background.js or popup.js, receiving that and change the dom in content.js. The following code demonstrate a simple scenario: Click the browserAction and append a div to the current page. You can use the same logic to show/hide any elements.

您应该从 background.js 或 popup.js 发送命令,接收该命令并更改 content.js 中的 dom。以下代码演示了一个简单的场景:单击 browserAction 并将一个 div 附加到当前页面。您可以使用相同的逻辑来显示/隐藏任何元素。

manifest.json

清单文件

{
  "name": "Test",
  "version": "1.0",
  "permissions": [
    "tabs"
  ],
  "description": "Test",
  "background": {
    "persistent": false,
    "scripts": [
      "background.js"
    ]
  },
  "content_scripts": [
    {
      "matches": [
        "*://*/*"
      ],
      "js": [
        "content.js"
      ],
      "run_at": "document_end",
      "all_frames": true
    }
  ],
  "browser_action": {
    "title": "Test"
  },
  "manifest_version": 2
}

background.js

背景.js

chrome.browserAction.onClicked.addListener(function() {
    chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
        chrome.tabs.sendMessage(tabs[0].id, {command: "append"}, function(response) {
            console.log(response.result);
        });
    });
});

content.js

内容.js

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse)    {
    console.log(request.command);

    var div = document.createElement('div');
    var label = document.createElement('span');
    label.textContent = "Hello, world";
    div.appendChild(label);
    document.body.appendChild(div);

    sendResponse({result: "success"});
});

回答by gaetanoM

I try to answer to this question as simple as ever, because making less changes to your code you will learn fast the path to follow.

我尽量像以往一样简单地回答这个问题,因为对代码进行较少的更改,您将快速了解要遵循的路径。

Normally I write the following lines of code when the end user press a START button on popup (refer to your popup.js):

通常,当最终用户在弹出窗口中按下 START 按钮时,我会编写以下代码行(请参阅您的 popup.js):

chrome.runtime.sendMessage({key: 'popupInit'}, function (response) {
 ;
});
window.close();  // this line closes the popup

What is very important is to understand the response is non a communication system but simply an answer on the fly coming from the corresponding listener. The listener for me is the background.js. In this file you may take advantage about the localStorage so that you can have something like:

非常重要的是要了解响应不是通信系统,而只是来自相应侦听器的即时响应。我的监听器是 background.js。在此文件中,您可以利用 localStorage 以便您可以拥有以下内容:

chrome.tabs.onUpdated.addListener(function(tabid, changeInfo, tab) {
  if (typeof changeInfo.status == 'string' && changeInfo.status == 'complete') {
    chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
      var keyString = JSON.parse(localStorage.getItem('keyWords'));
      chrome.tabs.sendMessage(tabs[0].id, {key: 'init', wordToHide: keyString}, function (response) {
        ;
      });
    });
  }
});

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
  var rq = request.key;
  if (rq != undefined && typeof rq == 'string') {
    switch (rq) {
      case 'popupInit':
        chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
          var keyString = JSON.parse(localStorage.getItem('keyWords'));
          chrome.tabs.sendMessage(tabs[0].id, msgToSend, function(response) {
            ;
          });
        });
        break;
    }
  }
});

The chrome.tabs.onUpdated.addListener is important because every time the active current page is updated you can send the message to the content script getting the values from the localstorage, the same happens when the popup is closed and if there are words in localstorage you can use the chrome.runtime.onMessage.addListener to listen for the signal: popup closed so start the work in the content script sending it a message with the array of words.

chrome.tabs.onUpdated.addListener 很重要,因为每次更新当前活动页面时,您都可以将消息发送到内容脚本以从 localstorage 获取值,当弹出窗口关闭并且 localstorage 中有单词时也会发生同样的情况您可以使用 chrome.runtime.onMessage.addListener 来侦听信号:弹出窗口已关闭,因此在内容脚本中开始工作,向其发送带有单词数组的消息。

Now let's take a look to your content.js file.

现在让我们看看您的 content.js 文件。

// Listen for messages from the backgound.js
chrome.runtime.onMessage.addListener(function (msg, sender, response) {
  // First, validate the message's structure
  if ((msg.key === 'init') && (Object.prototype.toString.call(msg.wordToHide) === '[object Array]')) {

    var elements = document.querySelectorAll("body, body *");
    var results = [];
    var child;
    var regwordToHide = [];
    if (msg.wordToHide.length > 0) {
      msg.wordToHide.forEach(function(element, index, array) {
        regwordToHide.push(new RegExp('\b' + element + '\b', 'g'));
      });
    }
    for(var i = 0; i < elements.length; i++) {
      child = elements[i].childNodes[0];
      if (elements[i].hasChildNodes() && child.nodeType == 3) {
        var nodeStr = child.textContent;
        if (nodeStr.trim().replace(/\n\r\t/g, '').length > 0 && nodeStr.trim().charAt(0) != '<') {
          regwordToHide.forEach(function(element, index, array) {
            nodeStr = nodeStr.replace(element, '');
          });
          child.textContent = nodeStr;
        }
      }
    }
    document.getElementsByTagName("html")[0].style.visibility = "visible";
  }
});
document.getElementsByTagName("html")[0].style.visibility = "hidden";

I try to hide the whole page: pay attention to this because it can be complicated (remember the end user must always see something while the page is loading....).

我尝试隐藏整个页面:请注意这一点,因为它可能很复杂(请记住,最终用户在页面加载时必须始终看到一些内容......)。

After, the content.js wait for a message coming from the background and when this happens the content script starts its work! That's all.

之后, content.js 等待来自后台的消息,当发生这种情况时,内容脚本开始工作!就这样。

Sorry for the confusion in my writing. If you need some other help let me know. I tested your ext and corrected it in the places you can see.

很抱歉我的写作混乱。如果您需要其他帮助,请告诉我。我测试了您的分机并在您可以看到的地方进行了更正。

For the communication between components in a chrome extension you need to remember:

对于 chrome 扩展中组件之间的通信,您需要记住:

  • the background is the big listener
  • the popup comunicate with the background
  • the background communicate with the content
  • 背景是大听众
  • 弹出窗口与背景沟通
  • 后台与内容交流

To send/receive messages you may take a look to Chrome messaging

要发送/接收消息,您可以查看Chrome 消息

  1. the function chrome.runtime.sendMessage({greeting: "hello"}, function(response) { console.log(response.farewell); }); is usend to send
  2. chrome.runtime.onMessage.addListener is the server (receiver)
  1. 函数 chrome.runtime.sendMessage({greeting: "hello"}, function(response) { console.log(response.farewell); }); 用于发送
  2. chrome.runtime.onMessage.addListener 是服务器(接收器)

The sendResponse and response are used just for a check on the fly regarding the communication: something like: Hello --> Ok I understood, Now I continue by myself.

sendResponse 和 response 仅用于检查有关通信的动态:类似于:您好 --> 好的,我明白了,现在我自己继续。

I completed also your word replacing part!

我也完成了你的话替换部分!