JSFiddle - React, Tailwind, and code Playground

by Palpatim

HTML

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<label>Rows: <input type="number" id="rows" value="5" /></label>
<label>Cols: <input type="number" id="cols" value="5" /></label>
<button id="start">Start</button>

<table id="grid">
    <tbody>
    </tbody>
</table>

CSS

/* Eric Meyer's Reset CSS v2.0 - http://cssreset.com */
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{border:0;font-size:100%;font:inherit;vertical-align:baseline;margin:0;padding:0}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:none}table{border-collapse:collapse;border-spacing:0}

body {
    font-size: 12px;
    font-family: sans-serif;
    padding: 1em;
}

input {
    width: 2em;
    padding: 0.1em;
    text-align: center;
}

label {
    margin-right: 2em;
}

#grid {
  border-collapse: collapse;
    margin: 3em auto;
    width: 90%;
    table-layout: fixed;
}

#grid td {
    border: 1px solid black;
}

.clicked {
    background: #fee;
}

JavaScript

/**
 * @param type the type of cell to create.
 * Allowed values: 'main', 'border'
 */
function _cell(spec) {
    spec = spec || {};
    var that = {};
    spec.cssClass = spec.cssClass || '';
    that.$el = $('<td class="' + spec.cssClass + '"/>');
    that.top = null;
    that.right = null;
    that.bottom = null
    that.left = null;
    return that;
}

function _mainCell(spec) {
    spec = spec || {cssClass: 'main'};
    var that = _cell(spec);
    that.owner = null;

    var clickHandler = function(e) {
        that.$el.addClass('clicked');
        that.$el.off('click');
    };
    that.clickHandler = clickHandler;

    that.$el.click(clickHandler);

    return that;
}

function _borderCell(spec) {
    spec = spec || {cssClass: 'border'};
    var that = _cell(spec);
    that.toggled = false;

    var clickHandler = function(e) {
        that.toggled = true;
        that.$el.addClass('toggled');
        that.off('click');
    };
    that.clickHandler = clickHandler;

    that.$el.click(clickHandler);

    return that;
}

function _row(spec) {
    spec = spec || {};
    var that = {};
    that.cells = [];
    that.$el = $('<tr />');

    var addCell = function(cell) {
        that.cells.push(cell);
        that.$el.append(cell.$el);
    };
    that.addCell = addCell;

    return that;
}

function createGrid() {
    console.log('createGrid.this::', this);
    var numRows = $('#rows').val();
    var numCols = $('#cols').val();
    var i, row, width,
        $tbody = $('#grid tbody');
    for (i = 0; i < numRows; i++) {
        row = createRow(numCols);
        $tbody.append(row.$el);
    }
    width = $tbody.outerWidth() / numCols;
    $tbody.find('.main').css({'width': width, 
         'height': width});
}

function createRow(numCols) {
    var i, row = _row();
    for (i = 0; i < numCols; i++) {
        row.addCell(_mainCell());
    }
    return row;
}

createGrid();