|
|
|
|
@ -0,0 +1,361 @@
|
|
|
|
|
<!doctype html>
|
|
|
|
|
<html lang="zh-CN">
|
|
|
|
|
<head>
|
|
|
|
|
<meta charset="UTF-8" />
|
|
|
|
|
<meta
|
|
|
|
|
name="viewport"
|
|
|
|
|
content="width=device-width, initial-scale=1.0"
|
|
|
|
|
/>
|
|
|
|
|
<title>DOM元素框选工具</title>
|
|
|
|
|
<style>
|
|
|
|
|
body {
|
|
|
|
|
margin: 0;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
font-family: Arial, sans-serif;
|
|
|
|
|
background-color: #f5f5f5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#container {
|
|
|
|
|
position: relative;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 80vh;
|
|
|
|
|
border: 2px solid #333;
|
|
|
|
|
background-color: white;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.selectable-element {
|
|
|
|
|
position: absolute;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
border: 1px solid #ccc;
|
|
|
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
|
|
|
transition: all 0.2s ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.selectable-element:hover {
|
|
|
|
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
|
|
|
|
transform: scale(1.02);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.selectable-element.selected {
|
|
|
|
|
border: 2px solid #007bff;
|
|
|
|
|
box-shadow: 0 0 10px rgba(0, 123, 255, 0.5);
|
|
|
|
|
z-index: 10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#selection-box {
|
|
|
|
|
position: absolute;
|
|
|
|
|
border: 2px dashed #007bff;
|
|
|
|
|
background-color: rgba(0, 123, 255, 0.1);
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#result {
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
padding: 15px;
|
|
|
|
|
background-color: #e9ecef;
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
min-height: 100px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
button {
|
|
|
|
|
margin: 5px;
|
|
|
|
|
padding: 10px 15px;
|
|
|
|
|
background-color: #007bff;
|
|
|
|
|
color: white;
|
|
|
|
|
border: none;
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
button:hover {
|
|
|
|
|
background-color: #0056b3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
h1 {
|
|
|
|
|
color: #333;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
|
<h1>DOM元素框选工具</h1>
|
|
|
|
|
|
|
|
|
|
<div id="container">
|
|
|
|
|
<!-- 可选择的元素 -->
|
|
|
|
|
<div
|
|
|
|
|
class="selectable-element"
|
|
|
|
|
style="width: 100px; height: 60px; top: 50px; left: 50px; background-color: #ff6b6b"
|
|
|
|
|
>
|
|
|
|
|
图片1
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
class="selectable-element"
|
|
|
|
|
style="width: 120px; height: 80px; top: 150px; left: 200px; background-color: #4ecdc4"
|
|
|
|
|
>
|
|
|
|
|
按钮1
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
class="selectable-element"
|
|
|
|
|
style="width: 150px; height: 70px; top: 300px; left: 100px; background-color: #45b7d1"
|
|
|
|
|
>
|
|
|
|
|
文本框
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
class="selectable-element"
|
|
|
|
|
style="width: 80px; height: 80px; top: 200px; left: 400px; background-color: #96ceb4"
|
|
|
|
|
>
|
|
|
|
|
图片2
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
class="selectable-element"
|
|
|
|
|
style="width: 100px; height: 50px; top: 100px; left: 500px; background-color: #feca57"
|
|
|
|
|
>
|
|
|
|
|
按钮2
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
class="selectable-element"
|
|
|
|
|
style="width: 130px; height: 90px; top: 350px; left: 400px; background-color: #ff9ff3"
|
|
|
|
|
>
|
|
|
|
|
文本区域
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
class="selectable-element"
|
|
|
|
|
style="width: 90px; height: 90px; top: 50px; left: 600px; background-color: #54a0ff"
|
|
|
|
|
>
|
|
|
|
|
图标
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
class="selectable-element"
|
|
|
|
|
style="width: 110px; height: 60px; top: 250px; left: 650px; background-color: #5f27cd"
|
|
|
|
|
>
|
|
|
|
|
标签
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
class="selectable-element"
|
|
|
|
|
style="width: 140px; height: 80px; top: 150px; left: 750px; background-color: #00d2d3"
|
|
|
|
|
>
|
|
|
|
|
输入框
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
class="selectable-element"
|
|
|
|
|
style="width: 100px; height: 100px; top: 300px; left: 700px; background-color: #ff6348"
|
|
|
|
|
>
|
|
|
|
|
按钮3
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 框选框 -->
|
|
|
|
|
<div id="selection-box"></div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<button onclick="clearSelection()">清除选择</button>
|
|
|
|
|
<button onclick="getSelectedElements()">获取选中元素</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div id="result">
|
|
|
|
|
<h3>选中元素列表:</h3>
|
|
|
|
|
<p id="selected-list">暂无选中元素</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
let isSelecting = false;
|
|
|
|
|
let startX, startY;
|
|
|
|
|
let selectionBox = document.getElementById('selection-box');
|
|
|
|
|
let container = document.getElementById('container');
|
|
|
|
|
let elements = document.querySelectorAll('.selectable-element');
|
|
|
|
|
|
|
|
|
|
// 鼠标按下事件
|
|
|
|
|
container.addEventListener('mousedown', function (e) {
|
|
|
|
|
// 如果点击的是元素本身,则不进行框选
|
|
|
|
|
if (e.target.classList.contains('selectable-element')) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
isSelecting = true;
|
|
|
|
|
// 获取相对于容器的坐标
|
|
|
|
|
const rect = container.getBoundingClientRect();
|
|
|
|
|
startX = e.clientX - rect.left;
|
|
|
|
|
startY = e.clientY - rect.top;
|
|
|
|
|
|
|
|
|
|
// 设置框选框的位置和大小
|
|
|
|
|
selectionBox.style.left = startX + 'px';
|
|
|
|
|
selectionBox.style.top = startY + 'px';
|
|
|
|
|
selectionBox.style.width = '0px';
|
|
|
|
|
selectionBox.style.height = '0px';
|
|
|
|
|
selectionBox.style.display = 'block'; // 防止拖拽时选择文字
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 鼠标移动事件
|
|
|
|
|
container.addEventListener('mousemove', function (e) {
|
|
|
|
|
if (!isSelecting) return;
|
|
|
|
|
|
|
|
|
|
const rect = container.getBoundingClientRect();
|
|
|
|
|
const currentX = e.clientX - rect.left;
|
|
|
|
|
const currentY = e.clientY - rect.top;
|
|
|
|
|
|
|
|
|
|
// 计算框选框的宽度和高度
|
|
|
|
|
const width = Math.abs(currentX - startX);
|
|
|
|
|
const height = Math.abs(currentY - startY);
|
|
|
|
|
|
|
|
|
|
// 确定框选框的左上角位置
|
|
|
|
|
const left = Math.min(startX, currentX);
|
|
|
|
|
const top = Math.min(startY, currentY);
|
|
|
|
|
|
|
|
|
|
// 更新框选框的样式
|
|
|
|
|
selectionBox.style.left = left + 'px';
|
|
|
|
|
selectionBox.style.top = top + 'px';
|
|
|
|
|
selectionBox.style.width = width + 'px';
|
|
|
|
|
selectionBox.style.height = height + 'px';
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 鼠标释放事件
|
|
|
|
|
container.addEventListener('mouseup', function (e) {
|
|
|
|
|
if (!isSelecting) return;
|
|
|
|
|
|
|
|
|
|
isSelecting = false;
|
|
|
|
|
|
|
|
|
|
// 获取框选范围(在隐藏之前)
|
|
|
|
|
const containerRect = container.getBoundingClientRect();
|
|
|
|
|
const selectionLeft = parseFloat(selectionBox.style.left);
|
|
|
|
|
const selectionTop = parseFloat(selectionBox.style.top);
|
|
|
|
|
const selectionWidth = parseFloat(selectionBox.style.width);
|
|
|
|
|
const selectionHeight = parseFloat(selectionBox.style.height);
|
|
|
|
|
|
|
|
|
|
// 修复:确保最小尺寸为1像素,避免0值问题
|
|
|
|
|
const finalWidth = Math.max(selectionWidth, 1);
|
|
|
|
|
const finalHeight = Math.max(selectionHeight, 1);
|
|
|
|
|
const finalLeft = selectionLeft;
|
|
|
|
|
const finalTop = selectionTop;
|
|
|
|
|
|
|
|
|
|
console.log('Selection box info:', {
|
|
|
|
|
left: finalLeft,
|
|
|
|
|
top: finalTop,
|
|
|
|
|
width: finalWidth,
|
|
|
|
|
height: finalHeight,
|
|
|
|
|
containerRect: containerRect,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 隐藏框选框
|
|
|
|
|
selectionBox.style.display = 'none';
|
|
|
|
|
|
|
|
|
|
// 检查哪些元素在框选范围内
|
|
|
|
|
checkSelection(finalLeft, finalTop, finalWidth, finalHeight);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 检查元素是否在框选范围内
|
|
|
|
|
function checkSelection(left, top, width, height) {
|
|
|
|
|
console.log('Checking selection:', { left, top, width, height });
|
|
|
|
|
|
|
|
|
|
const selectedElements = [];
|
|
|
|
|
|
|
|
|
|
elements.forEach((element) => {
|
|
|
|
|
const elementRect = element.getBoundingClientRect();
|
|
|
|
|
const containerRect = container.getBoundingClientRect();
|
|
|
|
|
|
|
|
|
|
// 转换为相对于容器的坐标
|
|
|
|
|
const elementLeft = elementRect.left - containerRect.left;
|
|
|
|
|
const elementTop = elementRect.top - containerRect.top;
|
|
|
|
|
const elementWidth = elementRect.width;
|
|
|
|
|
const elementHeight = elementRect.height;
|
|
|
|
|
|
|
|
|
|
console.log('Element info:', {
|
|
|
|
|
text: element.textContent,
|
|
|
|
|
left: elementLeft,
|
|
|
|
|
top: elementTop,
|
|
|
|
|
width: elementWidth,
|
|
|
|
|
height: elementHeight,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 矩形相交检测算法
|
|
|
|
|
// 两个矩形相交的条件是:在x轴和y轴上都有重叠
|
|
|
|
|
const xOverlap = elementLeft < left + width && elementLeft + elementWidth > left;
|
|
|
|
|
const yOverlap = elementTop < top + height && elementTop + elementHeight > top;
|
|
|
|
|
|
|
|
|
|
console.log('Overlap check:', {
|
|
|
|
|
xOverlap,
|
|
|
|
|
yOverlap,
|
|
|
|
|
condition: xOverlap && yOverlap,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (xOverlap && yOverlap) {
|
|
|
|
|
selectedElements.push(element);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
console.log('Selected elements count:', selectedElements.length);
|
|
|
|
|
|
|
|
|
|
// 标记选中的元素
|
|
|
|
|
markSelectedElements(selectedElements);
|
|
|
|
|
|
|
|
|
|
// 显示结果
|
|
|
|
|
displayResults(selectedElements);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 标记选中的元素
|
|
|
|
|
function markSelectedElements(elements) {
|
|
|
|
|
// 先清除所有已选中的标记
|
|
|
|
|
document.querySelectorAll('.selectable-element.selected').forEach((el) => {
|
|
|
|
|
el.classList.remove('selected');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 给新选中的元素添加标记
|
|
|
|
|
elements.forEach((element) => {
|
|
|
|
|
element.classList.add('selected');
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 显示结果
|
|
|
|
|
function displayResults(elements) {
|
|
|
|
|
const resultElement = document.getElementById('selected-list');
|
|
|
|
|
if (elements.length === 0) {
|
|
|
|
|
resultElement.innerHTML = '没有选中任何元素';
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let html = '<ul>';
|
|
|
|
|
elements.forEach((element, index) => {
|
|
|
|
|
html += `<li>${index + 1}. ${element.textContent || '未知元素'}</li>`;
|
|
|
|
|
});
|
|
|
|
|
html += '</ul>';
|
|
|
|
|
|
|
|
|
|
resultElement.innerHTML = html;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 清除选择
|
|
|
|
|
function clearSelection() {
|
|
|
|
|
document.querySelectorAll('.selectable-element.selected').forEach((el) => {
|
|
|
|
|
el.classList.remove('selected');
|
|
|
|
|
});
|
|
|
|
|
document.getElementById('selected-list').innerHTML = '暂无选中元素';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取选中元素
|
|
|
|
|
function getSelectedElements() {
|
|
|
|
|
const selectedElements = document.querySelectorAll('.selectable-element.selected');
|
|
|
|
|
const elementsArray = Array.from(selectedElements);
|
|
|
|
|
displayResults(elementsArray);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 添加鼠标离开容器时的处理,防止拖拽时鼠标移出容器导致的问题
|
|
|
|
|
document.addEventListener('mouseleave', function (e) {
|
|
|
|
|
if (isSelecting && e.target === container) {
|
|
|
|
|
isSelecting = false;
|
|
|
|
|
selectionBox.style.display = 'none';
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 添加document mouseup事件,防止鼠标在容器外释放时的问题
|
|
|
|
|
document.addEventListener('mouseup', function (e) {
|
|
|
|
|
if (isSelecting) {
|
|
|
|
|
isSelecting = false;
|
|
|
|
|
selectionBox.style.display = 'none';
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|