应用于可视化埋点需求,需要一个JS能够通过框选 获取页面的DOM元素
且记录Xpath路径,用于后续对Dom进行后续的操作
简单做了Demo,后续继续优化
<!doctype html>
<html>
<head>
<title>Js 可视化圈选Dom元素,Xpath路径变更Dom</title>
<style>
* {
margin: 0;
padding: 0;
}
.drawDiv {
position: absolute;
background: #ccc;
}
.selBlue {
border: 1px dashed blue !important;
}
.sel {
margin: 30px auto;
width: 960px;
overflow: hidden
}
li {
list-style: none;
float: left;
width: 60px;
height: 20px;
}
.selColor {
float: left
}
.xz {
width: 340px;
float: left;
}
#canvas {
width: 960px;
height: 500px;
border: 1px solid #ccc;
margin: 0 auto
}
</style>
<script>
function del(){
var xpath=document.getElementById("xPath").value;
var doc=getElem(xpath);
if(doc!=""){
var classVal = doc.getAttribute("class");
classVal=classVal.replace("selBlue");
doc.setAttribute("class",classVal);
}
};
function $Id(id) {
return document.getElementById(id);
}
function getXPath(elm) {
let allNodes = document.getElementsByTagName('*')
for (var segs = []; elm && elm.nodeType == 1; elm = elm.parentNode) {
if (elm.hasAttribute('id')) {
let uniqueIdCount = 0
for (var n = 0; n < allNodes.length; n++) {
if (allNodes[n].hasAttribute('id') && allNodes[n].id == elm.id) uniqueIdCount++;
if (uniqueIdCount > 1) break;
}
if (uniqueIdCount == 1) {
segs.unshift('//*[@id="' + elm.getAttribute('id') + '"]');
return segs.join('/');
} else {
return false
}
} else {
for (var i = 1, sib = elm.previousSibling; sib; sib = sib.previousSibling) {
if (sib.localName == elm.localName) i++;
}
if (i == 1) {
if (elm.nextElementSibling) {
if (elm.nextElementSibling.localName != elm.localName) {
segs.unshift(elm.localName.toLowerCase())
} else {
segs.unshift(elm.localName.toLowerCase() + '[' + i + ']');
}
} else {
segs.unshift(elm.localName.toLowerCase())
}
} else {
segs.unshift(elm.localName.toLowerCase() + '[' + i + ']');
}
}
}
return segs.length ? '/' + segs.join('/') : null
};
function getElem(path) {
try {
var evaluator = new XPathEvaluator();
var result = evaluator.evaluate(path, document.documentElement, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
return result.singleNodeValue || '';
} catch (e) {
console.log(e)
return ''
}
};
window.onload = function () {
var oCanvas = $Id('canvas');
// var oRadius = $Id('radius');
var aInputs = document.getElementsByTagName('input');
var xz = 'none';
var arr = [];
for (var i = 0; i < aInputs.length; i++) {
if (aInputs[i].type == 'checkbox') {
arr.push(aInputs[i]);
}
}
for (var i = 0; i < arr.length; i++) {
arr[i].onclick = function () {
if (!this.checked) {
this.checked = false;
} else {
for (var j = 0; j < arr.length; j++) {
arr[j].checked = false;
}
this.checked = true;
xz = this.value;
}
}
}
document.onclick = function (ev) {
for (var i = 0; i < arr.length; i++) {
if (arr[i].checked) {
arr[i].checked = true;
xz = arr[i].value;
if (xz != "none") {
return;
}
}
}
var x = ev.clientX;
var y = ev.clientY;
var delDocArr = document.elementsFromPoint(x, y);
for (var delDoc of delDocArr) {
var classVal = delDoc.getAttribute("class");
//如果圈选的内容已经存在了,则跳过
if (classVal!=null && classVal.indexOf("selBlue")>=0) {
console.log(delDoc);
// alert("选择了删除,删除圈选内容:" + delDoc.id);
console.log("选择了删除,删除圈选内容:" + delDoc.id);
// delDoc.remove();
var selDivName = document.getElementById("selDivName");
selDivName.value = delDoc.id;
var xPath = getXPath(delDoc);
document.getElementById("xPath").value = xPath;
}
}
}
oCanvas.onmousedown = function (ev) {
if (oCanvas.setCapture) {
oCanvas.setCapture();
}
for (var i = 0; i < arr.length; i++) {
if (arr[i].checked) {
arr[i].checked = true;
xz = arr[i].value;
if (xz != "select") {
return;
}
}
}
var oEv = ev || window.event;
var disX = oEv.clientX;
var disY = oEv.clientY;
var oR = document.createElement('div');
// oR.id = "div";
oR.className = "drawDiv"
oR.style.top = disY + 'px';
oR.style.left = disX + 'px';
// oR.style.backgroundColor = color;
oR.style.border = '1px dashed red';
oR.style.backgroundColor = 'transparent';
document.body.appendChild(oR);
document.onmousemove = function (ev) {
if (oCanvas.setCapture) {
oCanvas.setCapture();
}
var oEv = ev || window.event;
var x = oEv.clientX;
var y = oEv.clientY;
if (x < oCanvas.offsetLeft) {
x = oCanvas.offsetLeft;
} else if (x > oCanvas.offsetLeft + oCanvas.offsetWidth) {
x = oCanvas.offsetLeft + oCanvas.offsetWidth
}
if (y < oCanvas.offsetTop) {
y = oCanvas.offsetTop;
} else if (y > oCanvas.offsetTop + oCanvas.offsetHeight) {
y = oCanvas.offsetTop + oCanvas.offsetHeight
}
oR.style.width = Math.abs(x - disX) + 'px';
oR.style.height = Math.abs(y - disY) + 'px';
oR.style.top = Math.min(disY, y) + 'px';
oR.style.left = Math.min(disX, x) + 'px';
}
document.onmouseup = function (ev) {
if (oCanvas.setCapture) {
oCanvas.setCapture();
}
var x = event.clientX;
var y = event.clientY;
var divx1 = oCanvas.offsetLeft;
var divy1 = oCanvas.offsetTop;
var divx2 = oCanvas.offsetLeft + oCanvas.offsetWidth;
var divy2 = oCanvas.offsetTop + oCanvas.offsetHeight;
if (x < divx1 || x > divx2 || y < divy1 || y > divy2) {
console.log("在外面");
// return;
} else {
console.log("在里面");
if (xz == 'select') {
document.onmousemove = null;
document.onmouseout = null;
var leftX = parseInt(oR.style.left.replace("px", ""));
var leftY = parseInt(oR.style.top.replace("px", ""));
var rightX = leftX + parseInt(oR.style.width.replace("px", ""));
var rightY = leftY + parseInt(oR.style.height.replace("px", ""));
console.log("左上角坐标点xy:" + leftX + "," + leftY);
console.log("右下角坐标点xy:" + rightX + "," + rightY);
// var cc=0;
var domArr = [];
outer:
for (var i = leftX; i <= rightX; i += 5) {
for (var k = leftY; k <= rightY; k += 5) {
var ddArr = document.elementsFromPoint(i, k);
for (var add of ddArr) {
// cc++;
// console.log(cc)
var dlx = parseInt(add.offsetLeft);
var dly = parseInt(add.offsetTop);
var dWidth = parseInt(add.offsetWidth);
var dHight = parseInt(add.offsetHeight);
var drx = dlx + dWidth;
var dry = dly + dHight;
if ( leftX<=dlx && leftY <= dly && drx <= rightX && dry <= rightY) {
if (domArr.indexOf(add) == -1 && add.className != "selBlue") {
// console.log("dl:"+dlx+","+dly+"--dr:"+drx+","+dry);
// console.log("ol:"+leftX+","+leftY+"--or:"+rightX+","+rightY);
var classVal = add.getAttribute("class");
//如果圈选的内容已经存在了,则跳过
if (classVal != null) {
if (classVal.indexOf("selBlue") >= 0) {
break outer;
}
classVal = classVal.concat(" selBlue");
} else {
classVal = "selBlue";
}
domArr.push(add);
console.log(add);
console.log("圈中的DIV:" + add.id);
alert("圈中的DIV:" + add.id);
add.setAttribute("class", classVal);
var selDivName = document.getElementById("selDivName");
selDivName.value = add.id;
var xPath = getXPath(add);
document.getElementById("xPath").value = xPath;
break outer;
}
}
}
}
;
};
// console.log(document.elementsFromPoint(ev.clientX, ev.clientY));
// console.log(domArr)
oR.remove();
}
}
if (oCanvas.releaseCapture) {
oCanvas.releaseCapture();
}
}
return false;
}
}
</script>
</head>
<body>
<div class="sel">
<span class="selColor">选择操作类型:</span>
<p class="xz">
<input type="checkbox" checked="checked" value="none" id="none"/>无操作
<input type="checkbox" id="select" value="select"/>框选
<!-- <input type="checkbox" id="del" value="select"/>删除-->
</p>
</div>
<div id="canvas" style="float: left;margin-left: 10%">
<div style="width: 30%;height: 40%;border: 1px solid red;float: left" id="div1"></div>
<div style="width: 20%;height: 20%;margin-top: 25%;margin-left: 20% ;border: 1px solid black" id="div2">
<div style="width: 20%;height: 20%;margin-top: 20px;margin-left: 20% ;border: 1px solid black" id="div3">
</div>
</div>
<div style="width: 20%;height: 20%;margin-left: 60% ;border: 1px solid black" id="div4">
</div>
</div>
<div style="width: 30%;height: 40%;border: 1px solid red;float: left;margin-left: 10px" id="divrg">
选中的DIV <input type="text" value="" id="selDivName" style="width: 200px"/>
<br>
xPath路径 <input type="text" value="" id="xPath" style="width: 200px"/>
<br>
<button type="button" id="save">保存</button>
<button type="button" id="del" onclick="del()">删除</button>
</div>
</body>
</html>
效果图