65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 JavaScript 构建 HTML 网格

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.57/5 (4投票s)

2013年10月28日

CPOL

4分钟阅读

viewsIcon

53664

downloadIcon

485

本文介绍了一个用于在 HTML 页面上显示网格的 JavaScript 工具。

JSGid Image

引言

本帖的目的是介绍一个名为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&nbsp;&nbsp;<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 = "&nbsp;";
         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 元素(由函数创建)

  1. <div id="gridForm" >HTML 表单在此处</div>
  2. <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,如下所示

  1. 一个 id 为 "gridstyle" 的样式部分用于网格和表单 div(这仅在初始化时完成)。
  2. 另一个 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 中运行。
© . All rights reserved.