使用 JavaScript 构建 HTML 网格






3.57/5 (4投票s)
本文介绍了一个用于在 HTML 页面上显示网格的 JavaScript 工具。
引言
本帖的目的是介绍一个名为JSGrid的基于 JavaScript 的工具(实用程序、插件)。该工具会在浏览器打开 HTML 页面时,在页面上叠加一个网格。该网格本质上是一个 HTML 表格,具有可变数量的行和列(即,单元格(<td>)的宽度和高度由用户控制)。
该网格可以更方便地可视化块级元素的定位坐标和尺寸。因此,该工具对网页设计师非常有用,尤其是在使用固定布局或 CSS 定位的情况下。此外,源代码(以及此处提供的说明)可以作为使用 JavaScript 进行 DOM 和 CSS 操作的一个很好的示例。
可以通过此处运行该工具。
如何使用脚本
要使用该脚本,请将以下行添加到 HTML 页面的 <head> 部分
<script src="grid.js" ></script>
确保 src 属性指向文件 "grid.js" 的可访问位置。当页面在浏览器中加载时,网格(及其控件表单)将自动出现,如上图所示。
脚本如何工作
实现网格的一种方法是为每个单元格使用一个 HTML div。但是,我遇到了一些问题,并决定改用 HTML 表格。
以下列表显示了我的脚本源代码的开头部分。
// JSGrid Version 1.2: developed by Dr. Nasir Darwish (KFUPM), released on Nov. 4, 2013
var $gridStyle = "div#gridForm { z-index:21; color:black;padding:2px;margin:0;font-size:11pt;font-family:Tahoma;position:fixed; right:0; top:0; " +
"background:lightgray; border:1px solid gray; border-top:0;border-right:0;width:90px; height:90px;} " +
"div#gridForm input {font-size: 10pt; margin:3px; color:black;} " +
"div#gridForm input[type=text] { width:30px; padding-left:2px; } " +
"div#gridForm input[type=button] { padding:6px; margin:8px;border:1px solid gray } " +
"div#grid { z-index:20; font-size:3pt; position:absolute; left:-1px; top:-1px; background:transparent; padding:0;margin:0; } " +
"div#grid table { font-size:0.8em; border-collapse: collapse; border-spacing:0; padding:0;margin:0; table-layout:fixed; } " +
"div#grid table td { border:1px solid black; margin:0; padding:0; -moz-box-sizing:border-box; box-sizing:border-box;}" ;
var $gridFormHTML = "Height <input type='text' id='cellHeight' value='200' />" +
"Width <input type='text' id='cellWidth' value='100' />" +
"<input type='button' onclick='createGrid()' value='Set Grid' />"
function createGrid()
{
var gridForm = document.getElementById("gridForm");
if (gridForm ==null) // Initialization
{ var formdiv = document.createElement("div");
formdiv.id = 'gridForm';
formdiv.innerHTML = $gridFormHTML;
var docbody = document.body;
docbody.insertBefore(formdiv, docbody.firstChild);
var griddiv = document.createElement("div");
griddiv.id = 'grid';
griddiv.innerHTML = "<table id='gridTable' ></table>";
docbody.insertBefore(griddiv, docbody.firstChild);
var gridst = document.createElement("style");
gridst.id = 'gridstyle';
gridst.innerHTML = $gridStyle;
var gridExt = document.createElement("style");
gridExt.id = 'gridExtra';
document.head.appendChild(gridExt);
document.head.appendChild(gridst);
}
var cellWidth = parseInt(document.getElementById("cellWidth").value);
var cellHeight = parseInt(document.getElementById("cellHeight").value);
var griddiv = document.getElementById("grid");
if ((cellWidth==0) && (cellHeight==0))
{ griddiv.style.display = "none"; setCanvas(); return; }
griddiv.style.display ="block";
griddiv.style.width = window.innerWidth + "px";
// griddiv.style.height = window.innerHeight + "px";
if (cellWidth== 0) cellWidth = window.innerWidth;
if (cellHeight==0) cellHeight = window.innerHeight;
// Note: In JavaScript, int/int produces float
var cols= Math.floor(window.innerWidth/cellWidth);
var rows= Math.floor(window.innerHeight/cellHeight);
var lastcellWidth = window.innerWidth - cols*cellWidth;
var lastcellHeight = 0;
// alert(lastcellWidth);
if (cellWidth*cols < window.innerWidth) cols++;
if (cellHeight*rows < window.innerHeight)
{ lastcellHeight = window.innerHeight - rows*cellHeight;
rows++;
}
var tbody = document.getElementById("gridTable");
tbody.innerHTML = ""; // Delete previous content
for(var i=0; i < rows; i++)
{ var row = document.createElement("tr");
for(var j=0; j < cols; j++)
{ var cell= document.createElement("td");
if ((cols >1) && (j== cols-1)) cell.setAttribute("style","width:" + lastcellWidth + "px");
//cell.innerHTML = " ";
row.appendChild(cell);
}
tbody.appendChild(row);
}
var lastRowStyle = "";
if (lastcellHeight != 0) // decide the height of cells in lastRow
{ lastRowStyle = "#grid table tr:last-child td { height:"+ lastcellHeight + "px; }"; }
document.getElementById("gridExtra").innerHTML= "#grid table td { width:" + cellWidth +"px; height:"+ cellHeight + "px; } " + lastRowStyle ;
// Call setCanvas(), user should click "Set Grid" button if browser window is resized
setCanvas();
}
// Code for setCanvas() function is shown later
根据前面的代码,网格是一个带有空单元格(<td>
)的 HTML 表格。该表格包含在一个 id 为 "grid" 的 div 中。输入文本框和 "Set Grid" 按钮是 id 为 "gridForm" 的 div 的一部分。后者 div 使用固定定位将其固定在浏览器窗口的右上角。
代码主要由 CreateGrid()
函数以及以下行组成
window.addEventListener("load", createGrid, false);
因此,当页面加载完成时,将执行 CreateGrid()
函数。该函数依赖于两个 DIV 元素(由函数创建)
- <div id="gridForm" >HTML 表单在此处</div>
- <div id="grid" >HTML 表格用于网格在此处</div>
要创建 "gridForm
" div,我使用以下代码(此代码仅运行一次,仅当 "gridforom
" 不存在时)
var formdiv = document.createElement("div");
formdiv.id = 'gridForm';
formdiv.innerHTML = $gridFormHTML;
var docbody = document.body;
docbody.insertBefore(formdiv, docbody.firstChild);
要创建 "grid" div,我们使用以下代码
var griddiv = document.createElement("div");
griddiv.id = 'grid';
griddiv.innerHTML = "<table id='gridTable' ></table>";
docbody.insertBefore(griddiv, docbody.firstChild);
这两个 div 使用它们的 ID 属性作为选择器具有一些相关的 CSS 样式。我们使用两个样式部分(在代码中创建),每个部分都有自己的 ID,如下所示
- 一个 id 为 "gridstyle" 的样式部分用于网格和表单 div(这仅在初始化时完成)。
- 另一个 id 为 "gridExtra" 的样式部分(在初始化时创建,稍后填充)用于设置单元格的宽度和高度。
请注意,网格和 gridForm div 都被分配了很高的 z-index 值(grid 为 20,gridForm 为 21),以使它们出现在任何其他内容之上。为了防止页面内容被隐藏,网格被赋予了透明的背景颜色。
重要观察
我注意到浏览器通常不遵守指定的单元格宽度。为了使单元格宽度生效,我不得不向 <table> 样式添加 table-layout:fixed
,并向 <td> 样式添加 box-sizing:border-box;
(或 Firefox 为 -moz-box-sizing:border-box
)。此外,为了防止滚动条不必要地显示,我不得不将 "grid" div 的位置设置为 left:-1px; top:-1px;
。
跟踪鼠标点击
理想情况下,我们应该获取用户在网页上选择位置的 (x,y) 坐标;具体来说,识别连续点击的两个位置并在它们之间绘制一条线。这现在通过 setCanvas()
函数支持
// The code that follows is to setup canvas and handle click events
var lastX = -1;
var lastY;
function setCanvas()
{ // alert("in setCanvas");
lastX = -1;
var canvas = document.getElementById("myCanvas");
if (!canvas)
{ canvas = document.createElement("canvas");
canvas.id = 'myCanvas';
canvas.setAttribute("style","position:absolute; left:0; top:0;");
document.body.insertBefore(canvas, document.body.firstChild);
canvas.parentNode.addEventListener("click", handleClick, false);
//canvas.parentNode.setAttribute("onclick","handleClick(event)");
}
canvas.setAttribute("width", window.innerWidth);
canvas.setAttribute("height", window.innerHeight);
}
function handleClick()
{ oEvent = arguments[0];
// alert(oEvent.target.tagName);
var tagName =oEvent.target.tagName;
if (!((tagName=="TD") || (tagName=="CANVAS"))) return;
if (lastX==-1)
{ lastX = oEvent.pageX;
lastY = oEvent.pageY;
return;
}
var canvas = document.getElementById('myCanvas');
var ctx=canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle="#FF0000"; // a red line
ctx.lineWidth=1;
ctx.lineStyle="#ffff00";
ctx.beginPath();
ctx.moveTo(lastX,lastY);
ctx.lineTo(oEvent.pageX,oEvent.pageY);
ctx.stroke();
ctx.fillStyle="#CC00FF";
ctx.font="14px sans-serif";
ctx.fillText(lastX + "," + lastY , lastX-20, lastY+20);
ctx.fillText(oEvent.pageX + "," + oEvent.pageY, oEvent.pageX-20, oEvent.pageY+20);
lastX = oEvent.pageX;
lastY = oEvent.pageY;
}
setCanvas()
函数会在整个页面上覆盖一个画布。handleClick()
函数会捕获画布上的鼠标单击事件,并在最后两次连续点击的位置之间绘制一条线。
限制和建议
该工具(1.2 版)现在可以在最新版本的 IE(11 版)、Chrome(30 版)和 Firefox(MS Windows OS 24 版)中完美运行。在早期版本中,我曾使用 innerText
属性来设置样式元素。这在 Firefox 中不起作用。事实证明,使用 innerHTML
属性来设置样式元素可以在跨浏览器使用。
当前版本不处理resize事件,因为 window.innerWidth 和 window.innerHeight 在调整大小时会持续变化,导致 createGrid() 出现故障。作为一种变通方法,如果浏览器窗口被调整大小,可以点击 "Set Grid" 按钮来重绘 "正确" 的网格。
可以通过将单元格宽度 [高度] 设置为 0 来删除垂直 [水平] 网格线。如果单元格宽度和高度都设置为 0,则网格不会显示(在代码中,display 属性被设置为 "none
")。这允许使用浏览器开发工具检查页面元素,否则这些元素会被网格覆盖。
当然,让浏览器直接实现网格会更有效。为此,建议为块级元素引入CSS grid属性。这可以使用类似于 border 属性的语法。例如,CSS 规则 "grid: 10px 20px black;"
创建一个单元格宽度为 10px、单元格高度为 20px 且网格线颜色为黑色的网格。或者,可以使用展开的 CSS 语法指定该示例:"grid-cellwidth:10px;grid-cellheight:20px;grid-linecolor:black;"
。
历史
- 2013 年 10 月 26 日:1.0 版。
- 2013 年 11 月 2 日:1.1 版。添加了 setCanvas() 函数来设置画布和处理单击事件。
- 2013 年 11 月 4 日:1.2 版。增强了脚本以在 Firefox 中运行。