录制和重播javascript

时间:2011-11-11 12:25:44

标签: javascript javascript-events record replay

我知道可以录制鼠标移动,滚动和击键。但是对文档的更改呢?如何记录对文档的更改?

这是我的尝试。必须有一种更简单的方法来存储所有事件吗?

我感谢所有我能得到的提示!

<!DOCTYPE html>
<html>
<head>
<title>Record And replay javascript</title>
</head>
<body id="if_no_other_id_exist">

<div style="height:100px;background:#0F0" id="test1">click me</div>
<div style="height:100px;background:#9F9" class="test2">click me</div>
<div style="height:100px;background:#3F9" id="test3">click me</div>
<div style="height:100px;background:#F96" id="test4">click me</div>



<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>

$(document).ready(function() {
var the_time_document_is_redy = new Date().getTime();
var the_replay = '';


$('div').live("click", function (){
var the_length_of_visit = new Date().getTime() - the_time_document_is_redy;


// check if the element that is clicked has an id
if (this.id)
{

the_replay =
the_replay
+
"setTimeout(\"$('#"
+
this.id
+
"').trigger('click')\","
+
the_length_of_visit
+
");"
;


alert (
"The following javascript will be included in the file in the replay version:\n\n"
+ 
the_replay
) // end alert

} // end if



// if it does not have an id, check if the element that is clicked has an class
else if (this.className)
{

// find the closest id to better target the element (needed in my application)
var closest_div_with_id = $(this).closest('[id]').attr('id');

the_replay =
the_replay
+
"setTimeout(\"$('#"
+
closest_div_with_id
+
" ."
+
this.className
+
"').trigger('click')\","
+
the_length_of_visit
+
");"
;


alert (
"The following javascript will be included in the file in the replay version:\n\n"
+ 
the_replay
) // end alert

} // end if

});






// fall back if there are no other id's
$('body').attr('id','if_no_other_id_exist');


// example of how it will work in the replay version
setTimeout("$('#test1').trigger('click')",10000);

});
</script>
</body>
</html>

6 个答案:

答案 0 :(得分:20)

我对这个问题感到好奇并在这里实施了一个概念验证

https://jsbin.com/nemake/edit?js,output

使用演示

  • 尝试按录制,单击,再次按录制,然后单击播放。
  • 播放创建<iframe>,注入原始HTML并重播用户事件。
  • 要更改缩放,请更改源代码中的REPLAY_SCALE变量。
  • 要更改播放速度,请更改源代码中的SPEED变量。
  • NB我只在chrome 56上测试了jsbin。

实施细节:

  • 支持鼠标移动,点击和输入。我省略了其他(滚动,窗口调整大小,悬停,焦点等),但应该很容易扩展。
  • 在侦听文档上的事件时,事件侦听器通过使用捕获绕过任何event.stopPropagation()
  • 使用zoom CSS3以不同的分辨率显示播放。
  • 可以覆盖透明画布以绘制鼠标的跟踪线。我只使用一个简单的div,所以没有跟踪线。

注意事项:

  • 想象一下,我们正在真实网站上捕获用户事件。由于所提供的页面可以在现在和播放之间改变,因此在重放iframe中的记录时我们不能依赖客户端的服务器。相反,我们必须快照html,记录期间发出的所有ajax请求和资源请求。在演示中,我只是为了简单而快照HTML。但实际上,所有额外请求都必须在客户端页面上下载时实时存储在服务器上。此外,在回放期间,重要的是以与用户感知的相同的时间线回放请求。要模拟请求时间线,还必须存储每个请求的偏移量和持续时间。在客户端上下载所有页面请求时,将减慢客户端页面的速度。优化此上载的一种方法可以是在上传请求之前对请求的内容进行md5散列,如果服务器上已经存在md5散列,则不需要重新上载请求数据。此外,一个用户的会话可以使用此散列方法利用另一个用户上传的请求数据。

  • 上传所有活动时需要仔细考虑。由于将生成大量事件,这意味着大量数据。也许可以对事件进行一些压缩,例如失去一些不那么重要的mousemove事件。不应对每个事件发送上载请求以最小化请求数。应该缓冲事件,直到在上载每批事件之前达到缓冲区大小或超时。应该使用超时,因为用户可以随时关闭页面,从而丢失一些事件。

  • 在播放期间,应该模拟传出的POST请求,以防止在其他地方重复事件。

  • 在播放过程中,应该欺骗用户代理,但在渲染原始显示时这可能不可靠。

  • 自定义录制代码可能与客户端代码冲突。例如jQuery的。命名空间将被要求避免这种情况。

  • 可能存在一些边缘情况,其中键入和点击可能无法再现与原始HTML中相同的结果HTML。随机数,日期时间。虽然并非所有浏览器都支持,但可能需要变异观察者观察HTML变化。屏幕截图可能会在这里有用,但可能会证明OTT。

答案 1 :(得分:12)

仅使用Javascript重播用户操作是复杂问题

首先,你无法移动鼠标光标,也无法模拟鼠标悬停/悬停。因此,用户与页面的交互很大程度上消失了。

其次,一旦录制了动作,大多数时候它们必须在不同的环境中重播,而不是首先记录。我的意思是您可以使用较小的分辨率,不同的客户端浏览器,基于重播浏览器cookie等的不同内容重播屏幕上的操作。

如果您花时间研究可用于记录网站访问者操作的可用服务(http://clicktale.comhttp://userfly.com/,仅举几例),您会看到其中没有一个是能够完全重放用户操作,特别是当涉及到mouseover,ajax,复杂的JS小部件时。

关于检测对DOM所做更改的问题 - 正如Chris Biscardi在答案中所述,有mutation events,就是为此而设计的。但是,请记住,它们并未在每个浏览器中实现。也就是说,IE不支持它们(根据msdn http://blogs.msdn.com/b/ie/archive/2010/03/26/dom-level-3-events-support-in-ie9.aspx上的博客条目,它们将在IE 9中得到支持。)

根据用例,依赖这些事件可能适合您。

至于“更简单的方式来存储所有事件”。还有其他方法(语法方面),监听您选择的事件,但处理(=存储)它们不能以简单的方式处理,除非您想序列化整个事件对象,这不是一个好主意,如果您要将有关这些事件的信息发送到某个服务器来存储它们。您必须意识到这样一个事实,即在使用网站时会出现大量事件,因此需要存储大量潜在数据/发送到服务器。

我希望自己清楚明白,你会发现其中一些信息很有用。我自己参与了旨在做你想要实现的目标的项目,所以我知道一旦你开始深入研究这个主题,它会变得多么复杂。

答案 2 :(得分:3)

我相信你正在寻找突变事件。

http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113/events.html#Events-eventgroupings-mutationevents

以下是一些资源:

http://tobiasz123.wordpress.com/2009/01/19/utilizing-mutation-events-for-automatic-and-persistent-event-attaching/

http://forum.jquery.com/topic/mutation-events-12-1-2010

https://github.com/jollytoad/jquery.mutation-events

更新

回应评论,这是一个非常非常基本的实现:

//callback function
function onNodeInserted(){alert('inserted')}
//add listener to dom(in this case the body tag)
document.body.addEventListener ('DOMNodeInserted', onNodeInserted, false); 
//Add element to dom 
$('<div>test</div>').appendTo('body')

就像WTK所说,你正在进入复杂的领域。

答案 3 :(得分:2)

<强>记录

保存页面的初始DOM,从中删除脚本,还需要将所有相对URL更改为绝对URL。

然后,记录DOM突变和键盘/鼠标事件。

<强>重放

从初始保存的DOM开始,使用时间戳顺序应用突变和事件。

事实上,点击不会做任何事情,因为我们删除了任何脚本。但是因为我们保存了DOM更改,所以我们可以在点击后重播效果。

答案 4 :(得分:0)

最近,我们现在可以使用MutationObserver

  

MutationObserver为开发人员提供了一种对变化作出反应的方法   一个DOM。它被设计为替代在中定义的突变事件   DOM3事件规范。

慢速演示,因为console.log消息很大。

var mutationObserver = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    console.log(mutation)
  })
})
mutationObserver.observe(watchme, {
  attributes: true,
  characterData: true,
  childList: true,
  subtree: true,
  attributeOldValue: true,
  characterDataOldValue: true
})
<div id="watchme" contenteditable>
  Hello world!
</div>

答案 5 :(得分:0)

我在github上找到了这两个解决方案,可让您捕获事件,然后在远程服务器上重播它们。

https://github.com/ElliotNB/js-replay

以及更全面的解决方案

https://github.com/rrweb-io/rrweb

https://www.rrweb.io/#demos

两者都有演示,您可以尝试。