通过chrome.tabs.sendMessage发送DOM节点对象

Oz *_*lom 1 javascript google-chrome google-chrome-extension

我正在写一个Chrome扩展程序。我需要将元素对象从内容脚本传递到后台脚本。

目标:
扩展程序涉及记录和重播用户操作。
数据保存在扩展的本地存储中,每个标签的对象均不同(按标签ID)。
数据结构是一个列表,{x: x, y:y, element: element}
当用户想要重播时,我对列表上的每个对象使用循环,并.click()在元素上使用

内容脚本中的代码:

将消息发送到后台脚本的函数:

function addToEventHistory(cords) {
    console.log(cords)
    chrome.runtime.sendMessage({action: "addToEventHistory", cords: cords}, function(response) {
        return response;
    });
}
Run Code Online (Sandbox Code Playgroud)

获得元素并感知它的函数:

mouseClick: function(e) {
                var target = e.target || e.srcElement
                var clickEvent = {x: e.pageX, y: e.pageY, element: target}
                addToEventHistory(clickEvent)
            }
Run Code Online (Sandbox Code Playgroud)

后台脚本中的代码:

var tabId = sender.tab.id;
var existingRecords = JSON.parse(localStorage.getItem('record_'+tabId)) || [];
existingRecords.push(request.cords)
console.log(request.cords)
localStorage.setItem('record_'+tabId, JSON.stringify(existingRecords));
sendResponse();
Run Code Online (Sandbox Code Playgroud)

问题是我要发送的元素被接收为空对象。通知console.log发送和接收。输出为:

发送:

function addToEventHistory(cords) {
    console.log(cords)
    chrome.runtime.sendMessage({action: "addToEventHistory", cords: cords}, function(response) {
        return response;
    });
}
Run Code Online (Sandbox Code Playgroud)

接收:

mouseClick: function(e) {
                var target = e.target || e.srcElement
                var clickEvent = {x: e.pageX, y: e.pageY, element: target}
                addToEventHistory(clickEvent)
            }
Run Code Online (Sandbox Code Playgroud)

*元素对象为空,仅具有 _proto_

是什么原因?
我该如何解决这个问题?

看起来问题不是序列化DOM对象,因为该对象在发送之前看起来不错,而在接收之前看起来不好。

Mak*_*yen 6

您不能将DOM元素作为runtime.sendMessage()消息发送

runtime.sendMessage()中的消息必须是“一个可通过JSON定义的对象”。DOM元素/节点不支持JSON。因此,您无法发送它们。在你的情况,您要发送target的的click事件。

最终,要决定为什么在后台脚本中需要此信息,而不是尝试序列化DOM元素,需要执行的操作。

如果要标识元素,则需要确定唯一的选择器。一种方法是为元素分配唯一的ID,然后在您的消息中传递该ID。但是,这仅在您希望在该选项卡中加载页面期间引用DOM节点时才有效。显然,一旦浏览器离开页面或将页面加载到其他选项卡中,您分配的任何ID将不可用。因此,该替代方法仅可用于标识当前页面有效期内的元素。但是,对于只想存储实际DOM元素的应用程序,分配唯一的ID将是有效的解决方案。换句话说,存储DOM元素仅在页面有效期内有效,因此分配唯一ID在相同时间段(当前页面的有效期内)有效。

如果要使用在重新加载页面时可以唯一标识元素的方法,则需要使用不同于分配ID的方法。使用什么很大程度上取决于您要使用元素时的选择方式以及您希望选择相对于页面结构更改的弹性(例如,在结构动态的页面上,可能需要使用其他方法而不是在静态页面上使用)。

对于您的应用程序,要在其中记录和回放用户操作,则需要根据鼠标在页面中的位置还是基于用户启动事件的元素来确定是否要记录这些操作。对于用于记录/回放/模拟用户动作的应用程序/语言,这是一个普遍的问题。通常,向用户提供关于他们希望如何记录这种用户交互的选项(例如,按位置或元素)。如果选择仅通过事件发生时的鼠标位置来存储用户操作,则可以Document.elementFromPoint()用来确定现在是哪个元素此时将事件发送到该元素。但是,这样做时,您还需要跟踪文档的滚动状态,并根据当前显示内或相对于文档的鼠标位置来确定是否要存储鼠标的位置。