Select text in a column of an html table

What you're looking for is called Range object (TextRange in IE).

Update: Here's a working code to do what you're suggesting: http://jsfiddle.net/4BwGG/3/

While capturing cell contents, you can format them in any manner you wish. I'm simply appending a new line every time.

Note:

  • Works fine in FF 3 and above
  • IE (before 9) and Chrome do not support multiple selection.
  • Chrome doesn't highlight all cells (but captures all content). Same goes for IE9
  • IE 7 & 8 will throw an error.

An alternative is apply a CSS style that simulates highlighting on click of column header and loop through all cells to capture their content. Look and feel of this approach may differ from native selection's look (unless you somehow capture select event and alter the appearnce).

Then use jQuery copy plugin to copy them to clipboard.


You could have a div which gets populated with the column data on click and apply a css class to give the columns the appearence of being selected

something like this:

var $mytable = $("#mytable"),
    $copydiv = $("#copy_div");

$mytable.find("td").click(function(){

    //get the column index
    var $this = $(this),
        index = $this.parent().children().index($this);

    //find all cells in the same column
    $mytable.find("tr:nth-child(" + index + ")").removeClass("selected").each(function () {
        var $this = $(this);
        $this.addClass("selected");
        $copydiv.html($this.html() + "<br />");
    });
});

or you could have a separate table for each column, but I don't think that would be worth it.


Some code review tools implement this to allow copying & pasting code from one side of a side-by-side diff. I looked into how ReviewBoard pulls it off.

The gist is:

  1. When a column selection begins, style the cells in all other columns with user-select: none (and its prefixed variants, if necessary). This creates the appearance of a column selection. The other columns are still secretly selected, so you have to...
  2. Intercept the copy event and change its payload to reflect the contents of the selected column.

The ReviewBoard code to do this consists of this CSS and this JavaScript.

I pulled it out into a fairly minimal jsbin demo.

Here's the CSS to create the appearance of a single-column selection (you add the selecting-left class to the table when the left column is being selected, or selecting-right for the right):

.selecting-left  td.right,
.selecting-left  td.right *,
.selecting-right td.left,
.selecting-right td.left *,
  user-select: none;
}

.selecting-left  td.right::selection,
.selecting-left  td.right *::selection,
.selecting-right td.left::selection,
.selecting-right td.left *::selection,
  background: transparent;
}

Here's the JavaScript to intercept the copy event and plug in a single column's worth of data:

tableEl.addEventListener('copy', function(e) {
  var clipboardData = e.clipboardData;
  var text = getSelectedText();
  clipboardData.setData('text', text);
  e.preventDefault();
});

function getSelectedText() {
  var sel = window.getSelection(),
      range = sel.getRangeAt(0),
      doc = range.cloneContents(),
      nodes = doc.querySelectorAll('tr'),
      text = '';

  var idx = selectedColumnIdx;  // 0 for left, 1 for right

  if (nodes.length === 0) {
    text = doc.textContent;
  } else {
    [].forEach.call(nodes, function(tr, i) {
      var td = tr.cells[tr.cells.length == 1 ? 0 : idx];
      text += (i ? '\n' : '') + td.textContent;
    });
  }

  return text;
}

There's also some less interesting code to add the selecting-left and selecting-right classes at the start of a selection. This would require a bit more work to generalize to n-column tables.

This seems to work well in practice, but it's surprising how hard it is!