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:
- 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... - 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!