说在前面
平时进行页面开发的时候,你们都是怎么判断元素是否对齐的?之前我都是使用截图工具利用截图的边缘来判断两个元素在水平和垂直方向上是否对齐。
但是这样操作久了总觉得有些繁琐,所以最后直接写了一个书签插件,一键给页面加上网格线,这样元素是否对齐就一目了然了。

功能介绍
1.给页面添加网格线
点击书签文件即可一键生成网格线

2.调整网格大小
可以通过配置面板调整网格大小

3.调整网格线颜色
可以通过配置面板调整网格线颜色

4.调整网格线透明度
可以通过配置面板调整网格线透明度

关键代码
1.网格覆盖层与控制面板的创建
javascript
// 网格覆盖层
const gridOverlay = document.createElement("div");
gridOverlay.id = "custom-grid-overlay";
// 关键样式: fixed 定位铺满全屏, pointer-events:none 让鼠标可以穿透它, z-index 设为极高值
gridOverlay.style.cssText = "position:fixed; top:0; left:0; right:0; bottom:0; pointer-events:none; z-index:2147483646;";
// 控制面板
const controlPanel = document.createElement("div");
controlPanel.id = "grid-control-panel";
// 关键样式: 磨砂玻璃效果, 高 z-index, pointer-events:auto 使其可交互
controlPanel.style.cssText = "position:fixed; top:10px; right:10px; background:rgba(255, 255, 255, 0.8); backdrop-filter: blur(5px); ... z-index:2147483647; pointer-events:auto;";
2.网格绘制
javascript
function drawGrid() {
gridOverlay.innerHTML = ''; // 先清空旧网格
const width = window.innerWidth, height = window.innerHeight;
// 计算需要的行数和列数
const colCount = Math.ceil(width / config.gridSize);
const rowCount = Math.ceil(height / config.gridSize);
// 循环创建垂直线
for (let i = 0; i <= colCount; i++) {
const line = document.createElement("div");
line.style.cssText = `position:absolute; top:0; bottom:0; width:${config.lineWidth}px; left:${i * config.gridSize}px; background-color:${config.gridColor};`;
gridOverlay.appendChild(line);
}
// 循环创建水平线
// ... 逻辑类似 ...
}
3.配置面板拖拽功能
javascript
function startDrag(e) {
// ... (省略部分代码)
dragState.isDragging = true;
// 记录初始位置和鼠标/触摸点坐标
const panelRect = controlPanel.getBoundingClientRect();
const coords = getClientCoords(e);
dragState.startX = coords.x;
dragState.startY = coords.y;
dragState.initialLeft = panelRect.left;
dragState.initialTop = panelRect.top;
// 注册移动和结束事件
document.addEventListener("mousemove", onDrag);
document.addEventListener("mouseup", endDrag);
// ... (触摸事件)
}
function onDrag(e) {
if (!dragState.isDragging) return;
// 计算偏移量并更新面板位置
const coords = getClientCoords(e);
const deltaX = coords.x - dragState.startX;
const deltaY = coords.y - dragState.startY;
controlPanel.style.left = `${dragState.initialLeft + deltaX}px`;
controlPanel.style.top = `${dragState.initialTop + deltaY}px`;
}
function endDrag(e) {
if (!dragState.isDragging) return;
dragState.isDragging = false;
// 移除事件监听器
document.removeEventListener("mousemove", onDrag);
document.removeEventListener("mouseup", endDrag);
// ... (触摸事件)
}
怎么使用?
1.代码复制
复制下面压缩过的代码
javascript
javascript:!function(){var e;if(document.getElementById("custom-grid-overlay")){window.cleanupGridBookmarklet&&window.cleanupGridBookmarklet();return}let t={gridSize:20,gridColor:"rgba(255, 0, 0, 0.1)",lineWidth:1},i=!0,n=document.createElement("div");n.id="custom-grid-overlay",n.style.cssText="position:fixed; top:0; left:0; right:0; bottom:0; pointer-events:none; z-index:2147483646;";let r=document.createElement("div");r.id="grid-control-panel",r.style.cssText="position:fixed; top:10px; right:10px; background:rgba(255, 255, 255, 0.6); backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px); border:1px solid rgba(204, 204, 204, 0.5); border-radius:8px; z-index:2147483647; pointer-events:auto; font-family:Arial,sans-serif; font-size:12px; box-shadow:0 4px 12px rgba(0,0,0,0.2); width:220px;";let o=document.createElement("div");o.style.cssText="padding:8px 10px; background:rgba(245, 245, 245, 0.7); border-bottom:1px solid rgba(238, 238, 238, 0.7); cursor:move; display:flex; justify-content:space-between; align-items:center; user-select:none; touch-action:none; border-top-left-radius: 8px; border-top-right-radius: 8px;",o.innerHTML='<strong>网格控制</strong><button id="toggle-panel" style="border:none;background:none;cursor:pointer;padding:2px 5px;">▼</button>';let l=document.createElement("div");l.style.padding="10px",l.innerHTML=` <div style="margin-bottom:8px;"><label>网格大小 (px): </label><input type="number" value="${t.gridSize}" min="5" max="200" id="grid-size" style="width:60px;margin-left:5px;"></div> <div style="margin-bottom:8px;"><label>网格颜色: </label><input type="color" value="#ff0000" id="grid-color" style="width:30px;height:20px;padding:0;margin-left:5px;"></div> <div style="margin-bottom:8px;"><label>透明度: </label><input type="range" min="0" max="1" step="0.1" value="0.1" id="grid-alpha" style="width:100px;margin-left:5px;"></div> %60;let a=document.createElement("div");a.style.cssText="display:flex; gap:5px; margin-top:10px;";let d=document.createElement("button");d.textContent="应用设置",d.style.padding="5px 8px";let s=document.createElement("button");function p(){n.innerHTML="";let e=window.innerWidth,i=window.innerHeight,r=Math.ceil(e/t.gridSize),o=Math.ceil(i/t.gridSize);for(let l=0;l<=r;l++){let a=document.createElement("div");a.style.cssText=%60position:absolute; top:0; bottom:0; width:${t.lineWidth}px; left:${l*t.gridSize}px; background-color:${t.gridColor};%60,n.appendChild(a)}for(let d=0;d<=o;d++){let s=document.createElement("div");s.style.cssText=%60position:absolute; left:0; right:0; height:${t.lineWidth}px; top:${d*t.gridSize}px; background-color:${t.gridColor};%60,n.appendChild(s)}}s.textContent="关闭网格",s.style.padding="5px 8px",a.appendChild(d),a.appendChild(s),l.appendChild(a),r.appendChild(o),r.appendChild(l),document.body.appendChild(n),document.body.appendChild(r);let g={isDragging:!1,startX:0,startY:0,initialLeft:0,initialTop:0};function c(e){return e.touches&&e.touches.length?{x:e.touches[0].clientX,y:e.touches[0].clientY}:{x:e.clientX,y:e.clientY}}function u(e){if("INPUT"===e.target.tagName||"BUTTON"===e.target.tagName)return;e.preventDefault(),e.stopPropagation(),g.isDragging=!0,o.style.cursor="grabbing";let t=r.getBoundingClientRect(),i=c(e);g.startX=i.x,g.startY=i.y,g.initialLeft=t.left,g.initialTop=t.top,document.addEventListener("mousemove",m),document.addEventListener("mouseup",x),document.addEventListener("mouseleave",x),document.addEventListener("touchmove",m,{passive:!1}),document.addEventListener("touchend",x)}function m(e){if(!g.isDragging)return;e.preventDefault(),e.stopPropagation();let t=c(e),i=t.x-g.startX,n=t.y-g.startY,o=g.initialLeft+i,l=g.initialTop+n;o=Math.max(0,Math.min(o,window.innerWidth-r.offsetWidth)),l=Math.max(0,Math.min(l,window.innerHeight-r.offsetHeight)),r.style.right="auto",r.style.left=%60${o}px%60,r.style.top=%60${l}px%60}function x(e){g.isDragging&&(e.stopPropagation(),g.isDragging=!1,o.style.cursor="move",document.removeEventListener("mousemove",m),document.removeEventListener("mouseup",x),document.removeEventListener("mouseleave",x),document.removeEventListener("touchmove",m,{passive:!1}),document.removeEventListener("touchend",x))}d.onclick=function e(){t.gridSize=parseInt(document.getElementById("grid-size").value,10);let i=document.getElementById("grid-color").value,n=parseFloat(document.getElementById("grid-alpha").value),r=parseInt(i.substring(1,3),16),o=parseInt(i.substring(3,5),16),l=parseInt(i.substring(5,7),16);t.gridColor=%60rgba(${r},${o},${l},${n})%60,p()},s.onclick=b,document.getElementById("toggle-panel").addEventListener("click",function e(){i=!i,l.style.display=i?"block":"none",this.textContent=i?"▼":"▶"}),o.addEventListener("mousedown",u),o.addEventListener("touchstart",u,{passive:!1});let v,$=(e=p,function(...t){clearTimeout(v),v=setTimeout(()=>e.apply(this,t),250)});function b(){n.remove(),r.remove(),window.removeEventListener("resize",$),window.removeEventListener("orientationchange",$),delete window.cleanupGridBookmarklet}window.addEventListener("resize",$),window.addEventListener("orientationchange",$),window.cleanupGridBookmarklet=b,p()}();
2.创建新书签
在浏览器中,右键点击你的书签栏,选择"添加网页..."或"添加书签..."

3.编辑书签信息

将前面复制的代码粘贴到网址输入框里,点击保存即可
4.使用
在需要生成网格线的页面中点击保存的书签即可

完整格式化代码
对源码感兴趣的同学可以看看~
javascript
javascript:(function() {
function debounce(func, delay) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), delay);
};
}
if (document.getElementById("custom-grid-overlay")) {
if (window.cleanupGridBookmarklet) {
window.cleanupGridBookmarklet();
}
return;
}
let config = {
gridSize: 20,
gridColor: 'rgba(255, 0, 0, 0.1)',
lineWidth: 1
};
let isPanelOpen = true;
const gridOverlay = document.createElement("div");
gridOverlay.id = "custom-grid-overlay";
gridOverlay.style.cssText = "position:fixed; top:0; left:0; right:0; bottom:0; pointer-events:none; z-index:2147483646;";
const controlPanel = document.createElement("div");
controlPanel.id = "grid-control-panel";
controlPanel.style.cssText = "position:fixed; top:10px; right:10px; background:rgba(255, 255, 255, 0.8); backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px); border:1px solid rgba(204, 204, 204, 0.5); border-radius:8px; z-index:2147483647; pointer-events:auto; font-family:Arial,sans-serif; font-size:12px; box-shadow:0 4px 12px rgba(0,0,0,0.2); width:220px;";
const panelHeader = document.createElement("div");
panelHeader.style.cssText = "padding:8px 10px; background:rgba(245, 245, 245, 0.7); border-bottom:1px solid rgba(238, 238, 238, 0.7); cursor:move; display:flex; justify-content:space-between; align-items:center; user-select:none; touch-action:none; border-top-left-radius: 8px; border-top-right-radius: 8px;";
panelHeader.innerHTML = '<strong>网格控制</strong><button id="toggle-panel" style="border:none;background:none;cursor:pointer;padding:2px 5px;">▼</button>';
const panelBody = document.createElement("div");
panelBody.style.padding = "10px";
panelBody.innerHTML = `
<div style="margin-bottom:8px;"><label>网格大小 (px): </label><input type="number" value="${config.gridSize}" min="5" max="200" id="grid-size" style="width:60px;margin-left:5px;"></div>
<div style="margin-bottom:8px;"><label>网格颜色: </label><input type="color" value="#ff0000" id="grid-color" style="width:30px;height:20px;padding:0;margin-left:5px;"></div>
<div style="margin-bottom:8px;"><label>透明度: </label><input type="range" min="0" max="1" step="0.1" value="0.1" id="grid-alpha" style="width:100px;margin-left:5px;"></div>
`;
const panelFooter = document.createElement("div");
panelFooter.style.cssText = "display:flex; gap:5px; margin-top:10px;";
const applyButton = document.createElement("button");
applyButton.textContent = "应用设置";
applyButton.style.padding = "5px 8px";
const closeButton = document.createElement("button");
closeButton.textContent = "关闭网格";
closeButton.style.padding = "5px 8px";
panelFooter.appendChild(applyButton);
panelFooter.appendChild(closeButton);
panelBody.appendChild(panelFooter);
controlPanel.appendChild(panelHeader);
controlPanel.appendChild(panelBody);
document.body.appendChild(gridOverlay);
document.body.appendChild(controlPanel);
function drawGrid() {
gridOverlay.innerHTML = '';
const width = window.innerWidth, height = window.innerHeight;
const colCount = Math.ceil(width / config.gridSize), rowCount = Math.ceil(height / config.gridSize);
for (let i = 0; i <= colCount; i++) {
const line = document.createElement("div");
line.style.cssText = `position:absolute; top:0; bottom:0; width:${config.lineWidth}px; left:${i * config.gridSize}px; background-color:${config.gridColor};`;
gridOverlay.appendChild(line);
}
for (let i = 0; i <= rowCount; i++) {
const line = document.createElement("div");
line.style.cssText = `position:absolute; left:0; right:0; height:${config.lineWidth}px; top:${i * config.gridSize}px; background-color:${config.gridColor};`;
gridOverlay.appendChild(line);
}
}
function applySettings() {
config.gridSize = parseInt(document.getElementById("grid-size").value, 10);
const colorHex = document.getElementById("grid-color").value;
const alpha = parseFloat(document.getElementById("grid-alpha").value);
const r = parseInt(colorHex.substring(1, 3), 16), g = parseInt(colorHex.substring(3, 5), 16), b = parseInt(colorHex.substring(5, 7), 16);
config.gridColor = `rgba(${r},${g},${b},${alpha})`;
drawGrid();
}
function togglePanel() {
isPanelOpen = !isPanelOpen;
panelBody.style.display = isPanelOpen ? "block" : "none";
this.textContent = isPanelOpen ? "▼" : "▶";
}
const dragState = { isDragging: false, startX: 0, startY: 0, initialLeft: 0, initialTop: 0 };
function getClientCoords(e) {
return e.touches && e.touches.length ? { x: e.touches[0].clientX, y: e.touches[0].clientY } : { x: e.clientX, y: e.clientY };
}
function startDrag(e) {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'BUTTON') return;
e.preventDefault();
e.stopPropagation();
dragState.isDragging = true;
panelHeader.style.cursor = "grabbing";
const panelRect = controlPanel.getBoundingClientRect();
const coords = getClientCoords(e);
dragState.startX = coords.x;
dragState.startY = coords.y;
dragState.initialLeft = panelRect.left;
dragState.initialTop = panelRect.top;
document.addEventListener("mousemove", onDrag);
document.addEventListener("mouseup", endDrag);
document.addEventListener("mouseleave", endDrag);
document.addEventListener("touchmove", onDrag, { passive: false });
document.addEventListener("touchend", endDrag);
}
function onDrag(e) {
if (!dragState.isDragging) return;
e.preventDefault();
e.stopPropagation();
const coords = getClientCoords(e);
const deltaX = coords.x - dragState.startX;
const deltaY = coords.y - dragState.startY;
let newLeft = dragState.initialLeft + deltaX;
let newTop = dragState.initialTop + deltaY;
newLeft = Math.max(0, Math.min(newLeft, window.innerWidth - controlPanel.offsetWidth));
newTop = Math.max(0, Math.min(newTop, window.innerHeight - controlPanel.offsetHeight));
controlPanel.style.right = "auto";
controlPanel.style.left = `${newLeft}px`;
controlPanel.style.top = `${newTop}px`;
}
function endDrag(e) {
if (!dragState.isDragging) return;
e.stopPropagation();
dragState.isDragging = false;
panelHeader.style.cursor = "move";
document.removeEventListener("mousemove", onDrag);
document.removeEventListener("mouseup", endDrag);
document.removeEventListener("mouseleave", endDrag);
document.removeEventListener("touchmove", onDrag, { passive: false });
document.removeEventListener("touchend", endDrag);
}
applyButton.onclick = applySettings;
closeButton.onclick = cleanup;
document.getElementById("toggle-panel").addEventListener("click", togglePanel);
panelHeader.addEventListener("mousedown", startDrag);
panelHeader.addEventListener("touchstart", startDrag, { passive: false });
const debouncedDrawGrid = debounce(drawGrid, 250);
window.addEventListener("resize", debouncedDrawGrid);
window.addEventListener("orientationchange", debouncedDrawGrid);
function cleanup() {
gridOverlay.remove();
controlPanel.remove();
window.removeEventListener("resize", debouncedDrawGrid);
window.removeEventListener("orientationchange", debouncedDrawGrid);
delete window.cleanupGridBookmarklet;
}
window.cleanupGridBookmarklet = cleanup;
drawGrid();
})();
公众号
关注公众号『 前端也能这么有趣 』,获取更多有趣内容~
发送 加群 还能加入前端交流群,和大家一起讨论技术、分享经验,偶尔也能摸鱼聊天~
说在后面
🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。