Javascript 如何将数据从内容脚本传递到页面级?

Javascript 如何将数据从内容脚本传递到页面级?,javascript,jquery,google-chrome-extension,content-script,Javascript,Jquery,Google Chrome Extension,Content Script,我正在将我所有的js代码注入到首页,但它需要用于ui和其他内容的图片,这些图片只能在chrome.extension.getUrl的帮助下导入,并且只能从内容脚本中调用,因此我找到了大量关于如何将数据传递到内容页的建议,而对于如何将数据传递回来的建议却一无所知,这有可能吗 我的代码现在如下所示: 我的js代码,将与其他代码一起注入: var Content = {}; $(document).contentReady = function(content) { Content = con

我正在将我所有的js代码注入到首页,但它需要用于ui和其他内容的图片,这些图片只能在chrome.extension.getUrl的帮助下导入,并且只能从内容脚本中调用,因此我找到了大量关于如何将数据传递到内容页的建议,而对于如何将数据传递回来的建议却一无所知,这有可能吗

我的代码现在如下所示:

我的js代码,将与其他代码一起注入:

var Content = {};
$(document).contentReady = function(content) {
    Content = content;
    $(document).ready(function () {/*cool stuff here, that require content*/});
}
var event = new CustomEvent('LoadContent');
window.dispatchEvent(event);
内容脚本:

document.querySelector('head').appendChild(jsCode);
window.addEventListener("LoadContent", function(evt) {
    var content =
    {
        data:   "url(" + chrome.extension.getURL('content.smth') + ")"
    };
    document.contentReady(content);
}, false);
显然,我得到了
document.contentReady不是一个函数

但在谷歌搜索了大约2个小时后,在文档中声明函数是关于如何从内容脚本传回数据的唯一(!)建议。

您可以通过创建新的脚本标记将JS数据传递到页面。例如:

function injectScript(code) {
    var body = document.getElementsByTagName('body')[0];
    var s = document.createElement('script');
    s.innerHTML = code;
    body.appendChild(s);
}
injectScript('var foo = 2;');
因此,对于您的特定示例,您应该能够:

injectScript('document.contentReady({data: url(' + blahblah + '})');

不太好看(当您使用覆盖内容脚本时会发生什么情况?),但它可以工作。

您可以通过创建新的脚本标记将JS数据传递到页面。例如:

function injectScript(code) {
    var body = document.getElementsByTagName('body')[0];
    var s = document.createElement('script');
    s.innerHTML = code;
    body.appendChild(s);
}
injectScript('var foo = 2;');
因此,对于您的特定示例,您应该能够:

injectScript('document.contentReady({data: url(' + blahblah + '})');

不漂亮(当您使用覆盖内容脚本时是什么情况?),但它可以工作。

内容脚本
不与页面上的普通脚本共享窗口对象。他们都在不同的背景下工作

var submitEvent = function(category, action, label) {
  var eventObserverPlaceholder = document.getElementById('events-observer-placeholder'),
    $eventEl = $('<span></span>').attr({
      'data-category': category,
      'data-action': action,
      'data-label': label
    });

  eventObserverPlaceholder.appendChild($eventEl.get(0));
};
在本例中,您正在窗口上注册一个事件侦听器,并在其他上下文(窗口)上侦听该事件。因此,您的事件侦听器将永远不会被调用

然而,我可以看到,在内容脚本和普通脚本之间进行通信的另一种方法是使用
MutationObserver

主意
  • 定义具有某个Id的节点,在该节点下创建与事件对应的子节点
  • 在脚本中注册集合观察者
  • 从内容脚本中,添加数据为
    data-*
    api的节点
  • 实现示例 内容脚本
    var submitEvent = function(category, action, label) {
      var eventObserverPlaceholder = document.getElementById('events-observer-placeholder'),
        $eventEl = $('<span></span>').attr({
          'data-category': category,
          'data-action': action,
          'data-label': label
        });
    
      eventObserverPlaceholder.appendChild($eventEl.get(0));
    };
    
    链接
  • 工作示例: 我在Chrome扩展中使用了相同的方法将事件提交给Google Analytics

  • 内容脚本:
  • var submitEvent = function(category, action, label) {
      var eventObserverPlaceholder = document.getElementById('events-observer-placeholder'),
        $eventEl = $('<span></span>').attr({
          'data-category': category,
          'data-action': action,
          'data-label': label
        });
    
      eventObserverPlaceholder.appendChild($eventEl.get(0));
    };
    
  • 正常脚本:

  • 内容脚本
    不与页面上的普通脚本共享窗口对象。他们都在不同的背景下工作

    var submitEvent = function(category, action, label) {
      var eventObserverPlaceholder = document.getElementById('events-observer-placeholder'),
        $eventEl = $('<span></span>').attr({
          'data-category': category,
          'data-action': action,
          'data-label': label
        });
    
      eventObserverPlaceholder.appendChild($eventEl.get(0));
    };
    
    在本例中,您正在窗口上注册一个事件侦听器,并在其他上下文(窗口)上侦听该事件。因此,您的事件侦听器将永远不会被调用

    然而,我可以看到,在内容脚本和普通脚本之间进行通信的另一种方法是使用
    MutationObserver

    主意
  • 定义具有某个Id的节点,在该节点下创建与事件对应的子节点
  • 在脚本中注册集合观察者
  • 从内容脚本中,添加数据为
    data-*
    api的节点
  • 实现示例 内容脚本
    var submitEvent = function(category, action, label) {
      var eventObserverPlaceholder = document.getElementById('events-observer-placeholder'),
        $eventEl = $('<span></span>').attr({
          'data-category': category,
          'data-action': action,
          'data-label': label
        });
    
      eventObserverPlaceholder.appendChild($eventEl.get(0));
    };
    
    链接
  • 工作示例: 我在Chrome扩展中使用了相同的方法将事件提交给Google Analytics

  • 内容脚本:
  • var submitEvent = function(category, action, label) {
      var eventObserverPlaceholder = document.getElementById('events-observer-placeholder'),
        $eventEl = $('<span></span>').attr({
          'data-category': category,
          'data-action': action,
          'data-label': label
        });
    
      eventObserverPlaceholder.appendChild($eventEl.get(0));
    };
    
  • 正常脚本:

  • 没有任何东西可以阻止您进行基于
    CustomEvent
    的双向通信,它可以使用
    detail
    属性传递数据:

    // Page script
    window.addEventListener('RecieveContent', function(evt) {
      // do something cool with evt.detail
    });
    var event = new CustomEvent('LoadContent');
    window.dispatchEvent(event);
    
    // Content script
    window.addEventListener('LoadContent', function(evt) {
      content = /* ... */
      var event = new CustomEvent('RecieveContent', {detail: content});
      window.dispatchEvent(event);
    });    
    
    可以找到更深入的答案

    但是,您应该问问自己是否需要页面级脚本来查询数据,因为您完全可以控制数据的注入时间。确保代码已执行后,可以使用单向方法:

    // Page script
    window.addEventListener('RecieveContent', function(evt) {
      // do something cool with evt.detail
    });    
    
    // Content script
    jsCode.onload = function() {
      // This fires after the page script finishes executing
      content = /* ... */
      var event = new CustomEvent('RecieveContent', {detail: content});
      window.dispatchEvent(event);
    }
    document.querySelector('head').appendChild(jsCode);
    

    没有任何东西可以阻止您进行基于
    CustomEvent
    的双向通信,它可以使用
    detail
    属性传递数据:

    // Page script
    window.addEventListener('RecieveContent', function(evt) {
      // do something cool with evt.detail
    });
    var event = new CustomEvent('LoadContent');
    window.dispatchEvent(event);
    
    // Content script
    window.addEventListener('LoadContent', function(evt) {
      content = /* ... */
      var event = new CustomEvent('RecieveContent', {detail: content});
      window.dispatchEvent(event);
    });    
    
    可以找到更深入的答案

    但是,您应该问问自己是否需要页面级脚本来查询数据,因为您完全可以控制数据的注入时间。确保代码已执行后,可以使用单向方法:

    // Page script
    window.addEventListener('RecieveContent', function(evt) {
      // do something cool with evt.detail
    });    
    
    // Content script
    jsCode.onload = function() {
      // This fires after the page script finishes executing
      content = /* ... */
      var event = new CustomEvent('RecieveContent', {detail: content});
      window.dispatchEvent(event);
    }
    document.querySelector('head').appendChild(jsCode);
    

    虽然这是一个更优雅的解决方案,但您只需在页面中插入一个
    标记,然后访问主页的执行内容。@gladsocc此方法的优点是,您可以使用此方法在内容脚本和普通脚本之间进行通信。你只需要两个占位符。一开始我看起来有点困惑,但我要试试这个,似乎它会起作用,非常感谢您的回复!虽然这是一个更优雅的解决方案,但您只需在页面中插入一个
    标记,然后访问主页的执行内容。@gladsocc此方法的优点是,您可以使用此方法在内容脚本和普通脚本之间进行通信。你只需要两个占位符。一开始我看起来有点困惑,但我要试试这个,似乎它会起作用,非常感谢您的回复!好吧,好吧,看起来很简单,但我怎么能发现,数据已经收到了?好吧,看起来很简单,但我怎么能发现,数据已经收到了?@Madara我不同意重复的说法,但鉴于我的重新投票有可能立即重新开始,我想避免引起(新的)钻石的愤怒。我的理由是:您的副本只显示了一种回答问题的可能途径,而不是它的表述方式:“如何将数据传递回”。dupe目标无法覆盖其他方法(将方法注入
    窗口
    )。这是一个更为普遍的问题,应该取消这个骗局。你的决定似乎是基于现有的答案。你同意吗?@Xan:当然,如果你认为我的决定错了,那就重新打开吧。还有,不要害怕引起我的愤怒。我感谢所有对我的决定的批评。@MadaraUchiha我只是认为不发表评论就这样做是不够的。@Madara我不同意重复投票,但鉴于我的重新投票有可能立即重新投票,我希望避免引起(新的)钻石的愤怒。我的理由是:你的复制品