JSFiddle - React, Tailwind, and code Playground
by Amir Sharifian
HTML
<div class="container">
<div class="elements">
<button id="btn">
A (Click me)
</button>
<button id="btn2">
B (Right Click)
</button>
<button id="replay">
Replay and stop recording
</button>
<button id="play" disabled>
Play Again
</button>
</div>
<div id="action-logs">
<h1>
Actions log
</h1>
<table id="logs">
<thead>
<tr>
<th>Action</th>
<th>Xpath</th>
</tr>
</thead>
<tbody id="tbody">
</tbody>
</table>
</div>
</div>
CSS
body {
height: 1500px;
}
.container {
display: flex;
flex-direction: row;
justify-content: space-around;
}
#action-logs {
border-left: 1px solid;
padding-left: 50px;
height: 100vh;
min-width: 50%;
}
table {
border: solid 1px #DDEEEE;
border-collapse: collapse;
border-spacing: 0;
font: normal 13px Arial, sans-serif;
width: 100%;
}
table thead th {
background-color: #DDEFEF;
border: solid 1px #DDEEEE;
color: #336B6B;
padding: 10px;
text-align: left;
text-shadow: 1px 1px 1px #fff;
}
table tbody td {
border: solid 1px #DDEEEE;
color: #333;
padding: 10px;
text-shadow: 1px 1px 1px #fff;
}
JavaScript
const actions = [];
let replay = false;
let lastActionTime = 0;
// Sleep action
const sleep = (milliseconds) => {
return new Promise(resolve => setTimeout(resolve, milliseconds))
}
// Get element xpath
const getXPathForElement = (element) => {
const idx = (sib, name) => sib ?
idx(sib.previousElementSibling, name || sib.localName) + (sib.localName == name) :
1;
const segs = elm => !elm || elm.nodeType !== 1 ? [''] :
elm.id && document.getElementById(elm.id) === elm ? [`id("${elm.id}")`] : [...segs(elm.parentNode), `${elm.localName.toLowerCase()}[${idx(elm)}]`];
return segs(element).join('/');
}
const getElementByXpath = (path) => {
return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}
const throttle = (func, delay) => {
let prev = Date.now() - delay;
return (...args) => {
let current = Date.now();
if (current - prev >= delay) {
prev = current;
func.apply(null, args);
}
}
};
// Add log in table and actions array
const addLog = (data) => {
// Don't log replay button actions
if (data.data.xpath && data.data.xpath == 'id("replay")') {
return;
}
// Don't log on replay
if (replay) {
return;
}
const actionTime = new Date().getTime();
const delay = lastActionTime === 0 ? 0 : actionTime - lastActionTime;
// Store log in array
actions.push({
id: actions.length + 1,
...data,
delay
});
// Update last action time just for click and context menu
if (data.type == "click" || data.type == "contextmenu" || lastActionTime == 0) {
lastActionTime = actionTime;
}
// Add log in table
let table = document.getElementById("tbody");
let row = table.insertRow(0);
let cell1 = row.insertCell(0);
let cell2 = row.insertCell(1);
cell1.innerHTML = data.type;
cell2.innerHTML = data.data.xpath ? data.data.xpath : "-";
}
// Track mouse movement
const handleMousemove = (event) => {
addLog({
type: "move",
...