Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Build an HTML Grid Using JavaScript

3.57/5 (4 votes)
4 Nov 2013CPOL4 min read 53.2K   448  
The article describes a JavaScript utility to display a grid for HTML pages.

JSGid Image

Introduction

The purpose of this post is to present a JavaScript-based tool (utility, add-on) named JSGrid. The tool overlays the HTML page (when opened by a browser) with a grid. The grid is basically an HTML table with a varying number of rows and columns (i.e., the cell (<td>) width and height is controlled by the user).

The grid permits easier visualization of positioning coordinates and sizes of block elements. Thus, the tool can be useful for web designers, especially in situations where fixed-layout or CSS positioning is employed. Furthermore, the source code (and the explanation given here) can serve as a good example of using JavaScript for DOM and CSS manipulation.

The tool can be run from here.

How to Use the Script

To use the script, add the following line to the <head> section of your HTML page:

XML
<script src="grid.js" ></script>

Ensure that the src attribute points to an accessible location of the file "grid.js". When the page loads in the browser, the grid (and its control form) will automatically appear, as in the figure shown above.

How the Script Works

One approach to implement a grid is to use an HTML div for each cell. However, I have encountered few problems and decided to use an HTML table instead.

The following listing shows the beginning part of the source code of my script.

JavaScript
// 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' />"
JavaScript
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

In accordance with the preceding code, the grid is an HTML table with empty cells (<td>s). The table is enclosed within a div with id="grid". The input text-boxes and "Set Grid" button are part of a div with id="gridForm". This latter div uses fixed positioning to anchor it to the top-right corner of the browser window.

The code consists primarily of the function CreateGrid() followed by the line:

XML
window.addEventListener("load", createGrid, false);

Thus, the function CreateGrid() is executed when the page finishes loading. The function relies on two DIV elements (which are created by the function):

  1. <div id="gridForm" >The HTML form goes here</div>
  2. <div id="grid" >The HTML table for the grid goes here</div>

To create the "gridForm" div, I use the code (this code runs once, only if "gridforom" does not exist):

JavaScript
 var formdiv = document.createElement("div");
 formdiv.id = 'gridForm';
 formdiv.innerHTML = $gridFormHTML;
 var docbody = document.body;
 docbody.insertBefore(formdiv, docbody.firstChild);

To create the "grid" div, we use the following code:

JavaScript
var griddiv = document.createElement("div");
griddiv.id = 'grid';
griddiv.innerHTML = "<table id='gridTable' ></table>"; 
docbody.insertBefore(griddiv, docbody.firstChild);

The two divs have some associated CSS styles using their ID attributes as selectors. We use two style sections (created in code), each with its own id as follows:

  1. One style section with id="gridstyle" is for use by the grid and the form divisions (this is only done during initialization).
  2. The other style section with id="gridExtra" (created at initialization and filled later) is used to set the cell width and height.

Note that both the grid and gridForm divisions are assigned high z-index values (20 for grid and 21 for gridForm) to have them appear atop of any other content. To prevent the page content from being hidden, the grid is assigned a transparent background color.

Important Observations

I have noticed that browsers often do not honor the assigned cell width. To get the cell width working, I had to add table-layout:fixedto <table> styles and box-sizing:border-box;(or -moz-box-sizing:border-box for FireFox) to <td> styles. Also, to prevent scroll bars from unnecessarily showing, I had to set the position of "grid" div to left:-1px; top:-1px;.

Tracking Mouse Clicks   

It is desirable to get the (x,y)-coordinates of locations (on the web page) chosen by the user; specifically, identify two successively clicked locations and draw a line between them. This is now supported through the setCanvas() function:

JavaScript
// 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;
}

The setCanvas() function overlays a canvas over the entire page. The handleClick() function traps mouse click events on the canvas and draws a line between the last two successively clicked locations.

Limitations, suggestions

The tool (version 1.2) now works perfectly in latest versions of IE (version 11), Chrome (version 30), and Firefox (version 24 for MS Windows OS). In earlier versions I was using the innerText property for setting style elements. This does not work in FireFox. It turns out that using the innerHTML  property for setting style elements works cross browser.

The current version does not handle the resize event because  the window.innerWidth and window.innerHeight continuously change during resizing and cause createGrid() to malfunction. As a workaround, if the browser window is resized, the "Set Grid" button can be clicked to redraw the "correct" grid.

Vertical [horizontal] grid lines can be removed by setting the cell width [height] to 0. If both the cell width and height are set to 0, then the grid is not displayed (in the code, the display property is set to "none"). This allows for inspecting page elements (using the browser developer tool) that would otherwise be covered by the grid.

Certainly it is more efficient to have the grid implemented directly by the browser. For this, it is suggested that a CSS grid property be introduced for block elements. This can use syntax similar to the border property. As an example, the CSS rule "grid: 10px 20px black;" creates a grid with cell width of 10px, cell height of 20px and black color for the grid lines. Alternatively, the example can be specified using expanded CSS syntax: "grid-cellwidth:10px;grid-cellheight:20px;grid-linecolor:black;".

History

  • 26thOctober, 2013: Version 1.0.
  • 2nd November, 2013: Version 1.1. Added a function setCanvas() to setup canvas and handle click events.
  • 4th November, 2013: Version 1.2. Enhanced the script to  work in FireFox. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)