【问题标题】:Change DOM Content With Chrome Extension使用 Chrome 扩展更改 DOM 内容
【发布时间】:2016-03-31 18:35:06
【问题描述】:

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

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

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

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

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

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

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

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

我的 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

<!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

   $(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

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

// 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);
  }
});

【问题讨论】:

  • 在不查看整个代码的情况下,在后台页面中创建一个新的 DOM 并将其发送到内容脚本 not 听起来是个不错的计划 :) 它会破坏大多数页。 DOM 操作应该发生在适当的位置(例如,通过将具有必要逻辑的脚本注入页面)。
  • 只需将 DOM 操作逻辑放在内容脚本中并将关键字发送给它(而不是发送 HTML,在后台或弹出页面中过滤并发送回内容脚本)。
  • 不能完全解决你的问题,但我建议你看看这个解决方案来搜索文本容器stackoverflow.com/a/18089011/1705006。搜索直接父节点比 div 节点更高效、更符合逻辑。

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


【解决方案1】:

我会尽量简单地回答这个问题,因为对代码进行较少的更改会帮助您更快地学习。

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

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

非常重要的是要了解响应不是通信系统,而只是来自相应听众的即时回答。 我的听众是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;
    }
  }
});

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

现在让我们看看您的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";

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

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

对不起,我的写作中的混乱。如果您需要其他帮助,请告诉我。我测试了你的分机并在你能看到的地方更正了它。

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

  • 背景是大听众
  • 弹出窗口与后台通信
  • 后台与内容交流

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

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

sendResponse 和响应仅用于即时检查通信:类似于:您好 --> 好的,我明白了,现在我自己继续。

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

【讨论】:

    【解决方案2】:

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

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

    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 = ""
        }
    }
    }
    

    【讨论】:

    • vars getKeyWordsconfig 来自哪里?
    • 你真的只需要chrome.tabs.executeScript(null, {file: 'custom.js'});
    【解决方案3】:

    您应该从 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

    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

    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"});
    });
    

    【讨论】:

    • 你能改变你展示的例子来使用我的情况吗?
    猜你喜欢
    • 2013-11-14
    • 1970-01-01
    • 2023-04-06
    • 2016-11-28
    • 2011-12-22
    • 1970-01-01
    • 2021-12-15
    • 1970-01-01
    • 2014-06-05
    相关资源
    最近更新 更多