How can user adjust html table column widths during their html session ?
The table td elements contain mostly text input fields that are sensibly sized via css classes and the table will default to acodate them. This will probably result in some horizontal scrolling, but that is okay.
But during an editing session user may need to increase width of some columns to aid their editing, how do I do this it does't matter if these settings are lost next time they load the page.
I'm assuming I use Javascript to update the field width but how do I trigger this, what is the visual cue ?
<table>
<tr>
<th class="tableheading ui-corner-all">
<label>
#
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="largeinputfield">
Album
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="mediuminputfield">
Genre
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="mediuminputfield">
Album Artist
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="mediuminputfield">
Sort Album Artist
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="mediuminputfield">
Album Artists
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="mediuminputfield">
Sort Album Artists
</label>
</th>
</tr>
<tr>
<td class="tableheading ui-corner-all">
1
</td>
<td>
<input name="13ALBUM" value="The Orchestral Suites" class="largeinputfield" type="text">
</td>
<td>
<input name="13GENRE" value="" class="mediuminputfield" type="text">
</td>
<td>
<input name="13ALBUM_ARTIST" value="Johann Sebastian Bach; Academy of Ancient Music, Christopher Hogwood" class="mediuminputfield" type="text">
</td>
<td>
<input name="13ALBUM_ARTIST_SORT" value="Bach, Johann Sebastian; Academy of Ancient Music; Hogwood, Christopher" class="mediuminputfield" type="text">
</td>
<td>
<input name="13ALBUM_ARTISTS" value="Johann Sebastian Bach;;;Academy of Ancient Music;;;Christopher Hogwood" class="mediuminputfield" type="text">
</td>
<td>
<input name="13ALBUM_ARTISTS_SORT" value="Bach, Johann Sebastian;;;Academy of Ancient Music;;;Hogwood, Christopher" class="mediuminputfield" type="text">
</td>
</tr>
<tr>
<td class="tableheading ui-corner-all">
1
</td>
<td>
<input name="14ALBUM" value="The Orchestral Suites" class="largeinputfield" type="text">
</td>
<td>
<input name="14GENRE" value="" class="mediuminputfield" type="text">
</td>
<td>
<input name="14ALBUM_ARTIST" value="Johann Sebastian Bach; Academy of Ancient Music, Christopher Hogwood" class="mediuminputfield" type="text">
</td>
<td>
<input name="14ALBUM_ARTIST_SORT" value="Bach, Johann Sebastian; Academy of Ancient Music; Hogwood, Christopher" class="mediuminputfield" type="text">
</td>
<td>
<input name="14ALBUM_ARTISTS" value="Johann Sebastian Bach;;;Academy of Ancient Music;;;Christopher Hogwood" class="mediuminputfield" type="text">
</td>
<td>
<input name="14ALBUM_ARTISTS_SORT" value="Bach, Johann Sebastian;;;Academy of Ancient Music;;;Hogwood, Christopher" class="mediuminputfield" type="text">
</td>
</tr>
How can user adjust html table column widths during their html session ?
The table td elements contain mostly text input fields that are sensibly sized via css classes and the table will default to acodate them. This will probably result in some horizontal scrolling, but that is okay.
But during an editing session user may need to increase width of some columns to aid their editing, how do I do this it does't matter if these settings are lost next time they load the page.
I'm assuming I use Javascript to update the field width but how do I trigger this, what is the visual cue ?
<table>
<tr>
<th class="tableheading ui-corner-all">
<label>
#
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="largeinputfield">
Album
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="mediuminputfield">
Genre
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="mediuminputfield">
Album Artist
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="mediuminputfield">
Sort Album Artist
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="mediuminputfield">
Album Artists
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="mediuminputfield">
Sort Album Artists
</label>
</th>
</tr>
<tr>
<td class="tableheading ui-corner-all">
1
</td>
<td>
<input name="13ALBUM" value="The Orchestral Suites" class="largeinputfield" type="text">
</td>
<td>
<input name="13GENRE" value="" class="mediuminputfield" type="text">
</td>
<td>
<input name="13ALBUM_ARTIST" value="Johann Sebastian Bach; Academy of Ancient Music, Christopher Hogwood" class="mediuminputfield" type="text">
</td>
<td>
<input name="13ALBUM_ARTIST_SORT" value="Bach, Johann Sebastian; Academy of Ancient Music; Hogwood, Christopher" class="mediuminputfield" type="text">
</td>
<td>
<input name="13ALBUM_ARTISTS" value="Johann Sebastian Bach;;;Academy of Ancient Music;;;Christopher Hogwood" class="mediuminputfield" type="text">
</td>
<td>
<input name="13ALBUM_ARTISTS_SORT" value="Bach, Johann Sebastian;;;Academy of Ancient Music;;;Hogwood, Christopher" class="mediuminputfield" type="text">
</td>
</tr>
<tr>
<td class="tableheading ui-corner-all">
1
</td>
<td>
<input name="14ALBUM" value="The Orchestral Suites" class="largeinputfield" type="text">
</td>
<td>
<input name="14GENRE" value="" class="mediuminputfield" type="text">
</td>
<td>
<input name="14ALBUM_ARTIST" value="Johann Sebastian Bach; Academy of Ancient Music, Christopher Hogwood" class="mediuminputfield" type="text">
</td>
<td>
<input name="14ALBUM_ARTIST_SORT" value="Bach, Johann Sebastian; Academy of Ancient Music; Hogwood, Christopher" class="mediuminputfield" type="text">
</td>
<td>
<input name="14ALBUM_ARTISTS" value="Johann Sebastian Bach;;;Academy of Ancient Music;;;Christopher Hogwood" class="mediuminputfield" type="text">
</td>
<td>
<input name="14ALBUM_ARTISTS_SORT" value="Bach, Johann Sebastian;;;Academy of Ancient Music;;;Hogwood, Christopher" class="mediuminputfield" type="text">
</td>
</tr>
You can do something like this, where you can use javascript to add draggable areas (set to blue for visibility). If you mess around with this, you should get to the solution you're looking for.
The set width on the table is important, as it won't work properly without that.
This goes through and automatically appends a resizing trigger to each TH element. Mousedown on that element sets its parent TH to the active element, which allows the global mousemove event to change its size.
function each(arr, fn){
let i = arr.length
while(--i > -1){ fn(arr[i]) }
}
function px(val){ return [val, 'px'].join("") }
var resizeElement, startSize, startX
function beginResize(e){
killResize()
let th = e.target.parentElement
resizeElement = th
startSize = th.clientWidth
startX = e.pageX
}
function killResize(){
resizeElement = null
startSize = null
startX = null
}
each(document.querySelectorAll('th'), elem => {
let trigger = document.createElement('span')
trigger.className = 'resizeTrigger'
trigger.addEventListener('mousedown', beginResize)
elem.appendChild(trigger)
})
document.addEventListener('mousemove', e => {
if(resizeElement){
let diff = e.pageX - startX
resizeElement.style.width = px(startSize + diff)
}
})
document.addEventListener('mouseup', killResize)
table {
table-layout: fixed;
width: 100%;
}
th {
position: relative;
}
th > .resizeTrigger {
content: '';
position: absolute;
display: block;
width: 8px;
right: -4px;
top: 0;
height: 100%;
background: blue;
cursor: ew-resize;
}
input {
width: 100%;
}
<table>
<tr>
<th class="tableheading ui-corner-all">
<label>
#
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="largeinputfield">
Album
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="mediuminputfield">
Genre
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="mediuminputfield">
Album Artist
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="mediuminputfield">
Sort Album Artist
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="mediuminputfield">
Album Artists
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="mediuminputfield">
Sort Album Artists
</label>
</th>
</tr>
<tr>
<td class="tableheading ui-corner-all">
1
</td>
<td>
<input name="13ALBUM" value="The Orchestral Suites" class="largeinputfield" type="text">
</td>
<td>
<input name="13GENRE" value="" class="mediuminputfield" type="text">
</td>
<td>
<input name="13ALBUM_ARTIST" value="Johann Sebastian Bach; Academy of Ancient Music, Christopher Hogwood" class="mediuminputfield" type="text">
</td>
<td>
<input name="13ALBUM_ARTIST_SORT" value="Bach, Johann Sebastian; Academy of Ancient Music; Hogwood, Christopher" class="mediuminputfield" type="text">
</td>
<td>
<input name="13ALBUM_ARTISTS" value="Johann Sebastian Bach;;;Academy of Ancient Music;;;Christopher Hogwood" class="mediuminputfield" type="text">
</td>
<td>
<input name="13ALBUM_ARTISTS_SORT" value="Bach, Johann Sebastian;;;Academy of Ancient Music;;;Hogwood, Christopher" class="mediuminputfield" type="text">
</td>
</tr>
Convert all inputs to textareas, as there is no need to write a javascript solution. Apply correct css. Textareas have a resize option. You may want to customize it with JavaScript.
For resizable columns there are numerous ways. I spent the time making a shoddy solution and would rather do it pletely differently, but here is something to get you started...
It uses textareas instead of inputs. For each textarea in a column, resizing it will resize all textareas in that column to be the same width. Obviously the css needs tweaking as they don't perfectly align, so it's not a great solution.
A better solution would be to get the x position of each column divider, and be able to set these x positions as desired. But I will look into that another time.
let allInputs = document.querySelectorAll('input');
let cols = document.querySelectorAll('table tr th').length-1;
let isDown = false;
let elSelected = null;
let allTextareas = Array.from(allInputs).map((el, i)=>{
let newEl = document.createElement("textarea");
newEl.innerHTML = el.value;
newEl.addEventListener('mousedown', function(){
isDown = true;
elSelected = {el: this, i: i, colID: i%cols};
});
el.parentNode.appendChild(newEl);
el.parentNode.removeChild(el);
return {el: newEl, colID: i%cols};
});
document.addEventListener('mouseup', function(){
isDown = false;
elSelected = null;
});
let colWidth = 0;
document.addEventListener('mousemove', function(){
if(isDown && elSelected){
if(elSelected.el.clientWidth != colWidth){
colWidth = elSelected.el.clientWidth;
allTextareas.forEach((o, j)=>{
if(elSelected.i !== j && elSelected.colID === o.colID){
o.el.style.width = colWidth+'px';
}
});
}
}
});
textarea {
resize: horizontal;
}
<table>
<tr>
<th class="tableheading ui-corner-all">
<label>
#
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="largeinputfield">
Album
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="mediuminputfield">
Genre
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="mediuminputfield">
Album Artist
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="mediuminputfield">
Sort Album Artist
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="mediuminputfield">
Album Artists
</label>
</th>
<th class="tableheading ui-corner-all">
<label class="mediuminputfield">
Sort Album Artists
</label>
</th>
</tr>
<tr>
<td class="tableheading ui-corner-all">
1
</td>
<td>
<input name="13ALBUM" value="The Orchestral Suites" class="largeinputfield" type="text">
</td>
<td>
<input name="13GENRE" value="" class="mediuminputfield" type="text">
</td>
<td>
<input name="13ALBUM_ARTIST" value="Johann Sebastian Bach; Academy of Ancient Music, Christopher Hogwood" class="mediuminputfield" type="text">
</td>
<td>
<input name="13ALBUM_ARTIST_SORT" value="Bach, Johann Sebastian; Academy of Ancient Music; Hogwood, Christopher" class="mediuminputfield" type="text">
</td>
<td>
<input name="13ALBUM_ARTISTS" value="Johann Sebastian Bach;;;Academy of Ancient Music;;;Christopher Hogwood" class="mediuminputfield" type="text">
</td>
<td>
<input name="13ALBUM_ARTISTS_SORT" value="Bach, Johann Sebastian;;;Academy of Ancient Music;;;Hogwood, Christopher" class="mediuminputfield" type="text">
</td>
</tr>
<tr>
<td class="tableheading ui-corner-all">
1
</td>
<td>
<input name="14ALBUM" value="The Orchestral Suites" class="largeinputfield" type="text">
</td>
<td>
<input name="14GENRE" value="" class="mediuminputfield" type="text">
</td>
<td>
<input name="14ALBUM_ARTIST" value="Johann Sebastian Bach; Academy of Ancient Music, Christopher Hogwood" class="mediuminputfield" type="text">
</td>
<td>
<input name="14ALBUM_ARTIST_SORT" value="Bach, Johann Sebastian; Academy of Ancient Music; Hogwood, Christopher" class="mediuminputfield" type="text">
</td>
<td>
<input name="14ALBUM_ARTISTS" value="Johann Sebastian Bach;;;Academy of Ancient Music;;;Christopher Hogwood" class="mediuminputfield" type="text">
</td>
<td>
<input name="14ALBUM_ARTISTS_SORT" value="Bach, Johann Sebastian;;;Academy of Ancient Music;;;Hogwood, Christopher" class="mediuminputfield" type="text">
</td>
</tr>
Here is another solution. To allow the table to expand beyond the screen width, place it inside a div and set overflow: auto css for that div.
Define the table contents inside 'headers' and 'rows'.
//define table
const headers = [{name:'#', type:'label'}, {name:'Album', type:'input'}, {name:'Genre', type:'input'}, {name:'...', type:'input'}];
const cols = headers.length;
const rows = [
['1', 'The Orchestral Suites', 'Johann Sebastian Bach; Academy of Ancient Music, Christopher Hogwood', '...'],
['2', 'The Orchestral Suites', 'Johann Sebastian Bach; Academy of Ancient Music, Christopher Hogwood', '...']
];
//create table
const elHeaderRow = document.getElementById('headers');
const elTable = document.getElementById('table');
const allElements = document.getElementsByTagName("*");//for cursor
//create headers
headers.forEach(({name, type})=>{
const el = document.createElement('th');
el.textContent = name;
elHeaderRow.appendChild(el);
});
let cells = [];
//create rows
rows.forEach((row, rowID)=>{
const elRow = document.createElement('tr');
elRow.id = 'row-'+(rowID+1);
cells.push(...row.map((name, colID)=>{
const elCell = document.createElement('td');
const type = headers[colID].type;
const elRowContents = document.createElement(type);
if(type === 'input'){
elRowContents.value = name;
elRowContents.type = 'text';
} else {
elRowContents.textContent = name;
}
elCell.appendChild(elRowContents);
elRow.appendChild(elCell);
return {rowID:rowID, colID:colID, element: elCell};
}));
elTable.appendChild(elRow);
});
const container = document.getElementById('table-container');
//get positions of cells (x etc.)
function getColumnPositions(cells){
const positions = cells.map(cell=>{
const rect = cell.element.getBoundingClientRect();
return {x:rect.x, width:rect.width, right: rect.right, element: cell.element};
});
return positions;
}
let cellPositions = getColumnPositions(cells);
const dividerWidth = 4;
let cellHit = null;
let isDown = false;
let isResizing = false;
// on mouse move
document.addEventListener('mousemove', function(e){
const x = e.pageX;
const y = e.pageY;
let tag = document.elementFromPoint(x, y);
let bStickRight = false;
if(tag){
tag = tag.tagName;
}
const insideTable = (tag === "DIV" || tag === "BODY" || tag === "TABLE" || tag === "TD" || tag === "TH" || tag === "INPUT");
if(insideTable){
if(!cellHit){
isResizing = false;
}
if(cellHit && isResizing){
if(container.scrollLeft === container.scrollWidth-container.clientWidth){
bStickRight = true;
container.scrollLeft = container.scrollWidth;
}
cellHit.element.style.width = (x-cellHit.x)+'px';
cellHit.element.style.minWidth = (x-cellHit.x)+'px';
if(bStickRight){
container.scrollLeft = container.scrollWidth;
}
} else {
cellHit = null;
cellPositions.forEach(cell=>{
if(Math.abs(x-cell.right) < dividerWidth){
cellHit = cell;
}
});
}
} else {
cellHit = null;
}
if(cellHit){
Array.from(allElements).forEach((el)=>{
el.style.cursor = 'ew-resize';
el.style.userSelect = 'none';
});
} else {
Array.from(allElements).forEach((el)=>{
el.style.cursor = '';
el.style.userSelect = '';
});
}
}, false);
//on mouse down
document.addEventListener('mousedown', function(e){
isDown = true;
if(cellHit){
isResizing = true;
}
}, false);
//on mouse up
document.addEventListener('mouseup', function(e){
isDown = false;
isResizing = false;
cellPositions = getColumnPositions(cells);
Array.from(allElements).forEach((el)=>{
el.style.cursor = '';
el.style.userSelect = '';
});
}, false);
input[type="text"] {
min-width: 100%;
}
#table-container {
overflow: auto;
max-width: 85%;
}
td {
padding: 0 2px;
}
<div id="table-container">
<table id="table">
<tr id="headers">
</tr>
</table>
</div>