4
\$\begingroup\$

I am trying to teach myself JavaScript. I've read some online tutorials and watched some Douglas Crockford videos and decided to dive right in.

I made a little table editor in JavaScript: there's been quite a bit of fighting with the DOM but I think I've mostly figured this out.

Am I on the right track? Any comments on what I could do better or have misconceptions about?

HTML:

<!DOCTYPE html><html>
<head><title>Learning JS</title><style>
    div.body { padding-top: 30px; padding-left: 10%; }
    table { border-collapse:collapse; }
    td { border-top: 2px solid #ddd; padding: 0 16px; }
    td:focus { outline-style: none; box-shadow: 0px 0px 4px 4px #87CEEB }
</style>
</head>
<body><div class=body>
<table><tr>
    <th>Name</th>
    <th>Value</th>
     <th>Col3</th>
    <th>Col4</th>
    <th>Col5</th>
</tr><tr>
    <td></td>
    <td>Testing, testing, 123</td>
    <td>Another Column</td>
    <td>test</td>
    <td>end</td>
</tr><tr>
    <td></td>
    <td>This Time</td>
    <td>nothing else</td>
    <td>testing some more</td>
    <td></td>
</tr><tr>
    <td>3</td>
    <td>This other time</td>
    <td>nothing more</td>
    <td>test this</td>
    <td></td>
</tr>
</table>
</div>
<script src=te.js></script>
</body>
</html>

JavaScript:

"use strict";
window.onload = function() {
    var firstColumn;
    var lastColumn;
    {
        let tables=document.getElementsByTagName("table");
        var width=tables[0].rows[0].cells.length;
        let td=document.getElementsByTagName("td");
        for (let i=0,len=td[0].parentNode.cells.length;i<len;++i) {
            if (firstColumn===undefined) firstColumn=i;
            lastColumn=i;
        }
        for (let i = 0;i < td.length; ++i) {
            td[i].onkeydown = kbhandle;
            if (!td[i].className) td[i].setAttribute("contentEditable", true);
        }
    }
    var tabpos=1;
    function kbhandle(e) {
        switch (e.which) {
            case 9:
                if (tabpos>this.cellIndex) tabpos=this.cellIndex;
                break;
            case 13:
                if (e.shiftKey || e.altKey || e.ctrlKey) break;
                return function(that) {
                    let row=that.parentNode;
                    if (row.rowIndex < row.parentNode.rows.length-1) row.nextSibling.cells[tabpos].focus();
                    return false;
                } (this);
            case 37:    //Left Arrow
                return function(that,sel) {
                    if (e.altKey || (!sel.anchorOffset && (sel.focusNode.tagName==='TD' || sel.focusNode.previousSibling === null))) {
                        if (that.cellIndex > firstColumn) {
                            tabpos=that.cellIndex-1;
                            that.parentNode.cells[tabpos].focus();
                            if (sel.rangeCount) {
                                let node;
                                let range = sel.getRangeAt(0);
                                if (node = that.parentNode.cells[that.cellIndex-1].lastChild) {
                                    range.setStart(node,node.length);
                                    range.setEnd(node,node.length);
                                    sel.removeAllRanges();  //needed for Webkit
                                    sel.addRange(range);    //needed for Webkit
                                }
                            }
                        }
                        return false;
                    }
                    return true;
                } (this,window.getSelection());
            case 38:    //Up Arrow
                return function(that,sel) {
                    if (e. ctrlKey || e.altKey || (!sel.anchorOffset && (sel.focusNode.tagName==='TD' || sel.focusNode.previousSibling === null))) {
                        tabpos=that.cellIndex;
                        that.parentNode.previousSibling.cells[that.cellIndex].focus();
                        if (sel.rangeCount) {
                            let node;
                            let range = sel.getRangeAt(0);
                            if ((node = that.parentNode.previousSibling.cells[that.cellIndex].lastChild) && node.parentElement.tagName === 'TD') {
                                range.setStart(node,node.length);
                                range.setEnd(node,node.length);
                                sel.removeAllRanges();  //needed for Webkit
                                sel.addRange(range);    //needed for Webkit
                            }
                        }
                        return false;
                    }
                    return true;
                } (this,window.getSelection());
            case 39:    //Right Arrow
                return function(that,sel) {
                    let len=sel.focusNode.textContent.length;
                    if (that.cellIndex<lastColumn && (e.altKey || !len || (len === sel.anchorOffset && sel.focusNode.nextSibling === null))) {
                        tabpos=that.cellIndex+1;
                        that.parentNode.cells[that.cellIndex+1].focus();
                        return false;
                    }
                    return true;
                } (this,window.getSelection());
            case 40:    //Down Arrow
                return function(that,sel) {
                    let len=sel.focusNode.textContent.length;
                    if (e. ctrlKey || e.altKey || !len || (len === sel.anchorOffset && sel.focusNode.nextSibling === null)) {
                        let row=that.parentNode;
                        tabpos=that.cellIndex;
                        if (row.rowIndex < row.parentNode.rows.length-1) row.nextSibling.cells[that.cellIndex].focus();
                        return false;
                    }
                    return true;
                } (this,window.getSelection());
        }
        return true;
    }
}
\$\endgroup\$
2
\$\begingroup\$

I've embedded your code into a Code Snippet below, so anyone can easily see your code running. I've also put it into a jsFiddle so that it can be easily edited.

"use strict";

var firstColumn;
var lastColumn; {
  let tables = document.getElementsByTagName("table");
  var width = tables[0].rows[0].cells.length;
  let td = document.getElementsByTagName("td");
  for (let i = 0, len = td[0].parentNode.cells.length; i < len; ++i) {
    if (firstColumn === undefined) firstColumn = i;
    lastColumn = i;
  }
  for (let i = 0; i < td.length; ++i) {
    td[i].onkeydown = kbhandle;
    if (!td[i].className) td[i].setAttribute("contentEditable", true);
  }
}
var tabpos = 1;

function kbhandle(e) {
  switch (e.which) {
    case 9:
      if (tabpos > this.cellIndex) tabpos = this.cellIndex;
      break;
    case 13:
      if (e.shiftKey || e.altKey || e.ctrlKey) break;
      return function(that) {
        let row = that.parentNode;
        if (row.rowIndex < row.parentNode.rows.length - 1) row.nextSibling.cells[tabpos].focus();
        return false;
      }(this);
    case 37: //Left Arrow
      return function(that, sel) {
        if (e.altKey || (!sel.anchorOffset && (sel.focusNode.tagName === 'TD' || sel.focusNode.previousSibling === null))) {
          if (that.cellIndex > firstColumn) {
            tabpos = that.cellIndex - 1;
            that.parentNode.cells[tabpos].focus();
            if (sel.rangeCount) {
              let node;
              let range = sel.getRangeAt(0);
              if (node = that.parentNode.cells[that.cellIndex - 1].lastChild) {
                range.setStart(node, node.length);
                range.setEnd(node, node.length);
                sel.removeAllRanges(); //needed for Webkit
                sel.addRange(range); //needed for Webkit
              }
            }
          }
          return false;
        }
        return true;
      }(this, window.getSelection());
    case 38: //Up Arrow
      return function(that, sel) {
        if (e.ctrlKey || e.altKey || (!sel.anchorOffset && (sel.focusNode.tagName === 'TD' || sel.focusNode.previousSibling === null))) {
          tabpos = that.cellIndex;
          that.parentNode.previousSibling.cells[that.cellIndex].focus();
          if (sel.rangeCount) {
            let node;
            let range = sel.getRangeAt(0);
            if ((node = that.parentNode.previousSibling.cells[that.cellIndex].lastChild) && node.parentElement.tagName === 'TD') {
              range.setStart(node, node.length);
              range.setEnd(node, node.length);
              sel.removeAllRanges(); //needed for Webkit
              sel.addRange(range); //needed for Webkit
            }
          }
          return false;
        }
        return true;
      }(this, window.getSelection());
    case 39: //Right Arrow
      return function(that, sel) {
        let len = sel.focusNode.textContent.length;
        if (that.cellIndex < lastColumn && (e.altKey || !len || (len === sel.anchorOffset && sel.focusNode.nextSibling === null))) {
          tabpos = that.cellIndex + 1;
          that.parentNode.cells[that.cellIndex + 1].focus();
          return false;
        }
        return true;
      }(this, window.getSelection());
    case 40: //Down Arrow
      return function(that, sel) {
        let len = sel.focusNode.textContent.length;
        if (e.ctrlKey || e.altKey || !len || (len === sel.anchorOffset && sel.focusNode.nextSibling === null)) {
          let row = that.parentNode;
          tabpos = that.cellIndex;
          if (row.rowIndex < row.parentNode.rows.length - 1) row.nextSibling.cells[that.cellIndex].focus();
          return false;
        }
        return true;
      }(this, window.getSelection());
  }
  return true;
}
div.body {
  padding-top: 30px;
  padding-left: 10%;
}

table {
  border-collapse: collapse;
}

td {
  border-top: 2px solid #ddd;
  padding: 0 16px;
}

td:focus {
  outline-style: none;
  box-shadow: 0px 0px 4px 4px #87CEEB
}
<!DOCTYPE html>
<html>

<head>
  <title>Learning JS</title>
</head>

<body>
  <div class=body>
    <table>
      <tr>
        <th>Name</th>
        <th>Value</th>
        <th>Col3</th>
        <th>Col4</th>
        <th>Col5</th>
      </tr>
      <tr>
        <td></td>
        <td>Testing, testing, 123</td>
        <td>Another Column</td>
        <td>test</td>
        <td>end</td>
      </tr>
      <tr>
        <td></td>
        <td>This Time</td>
        <td>nothing else</td>
        <td>testing some more</td>
        <td></td>
      </tr>
      <tr>
        <td>3</td>
        <td>This other time</td>
        <td>nothing more</td>
        <td>test this</td>
        <td></td>
      </tr>
    </table>
  </div>
</body>

</html>

Lets start with the first chunk of code:

var firstColumn;
var lastColumn;
{
    let tables=document.getElementsByTagName("table");
    var width=tables[0].rows[0].cells.length;
    let td=document.getElementsByTagName("td");
    for (let i=0,len=td[0].parentNode.cells.length;i<len;++i) {
        if (firstColumn===undefined) firstColumn=i;
        lastColumn=i;
    }
    for (let i = 0;i < td.length; ++i) {
        td[i].onkeydown = kbhandle;
        if (!td[i].className) td[i].setAttribute("contentEditable", true);
    }
}

The extra block statement makes the code slightly harder to read. It's important to remember that tables and td will not be accessible outside the block, but width will. That might be the intended behavior here, but using extra blocks like this could easily get you trouble.

The width variable is never used after being set.

It would seem like firstColumn is always set to 0 no matter what. Setting it with an if statement during the for loop is unnecessary. You can just set it to 0 when you initialize it.

In fact that whole first for loop is unnecessary, since you can just set last column to td[0].parentNode.cells.length -1

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.