I don't know if my question is simple or naive, but I am still learning javascript, so be lenient with me.
One of my exercise was to create a DIV element with a specific width and height and then using javascript I tried to generate table inside this DIV. I thought that this is a simple problem, just take parent div width and height and divide it by the number of columns and rows. What I found is that in horizontal direction this works fine, but in vertical direction table height is bigger than the sum of cells heigh.
Let say that div height is 407px. This means that cell height (assuming 15 rows) should be 27,13px. The total height of the table should be 15*27,13=407px. But table.clientWidth gives me 420px in chrome and 453px in IE10.
I thought about borders which may be taken into account, but why then I get right width? I tried to use getComputedStyle() method instead element.clientWidth but the results were the same. Then I have read about logical vs physical pixels, but this was not helpful. I don't understand why this code behaves in this way? What I am missing?
My HTML
<div id="container">
<div id="target"></div>
</div>
My JavaScript
var createGrid = function(rows, cols, appendToID){
var appendTo = document.getElementById(appendToID);
var appendToWidth = appendTo.clientWidth;
var appendToHeight = appendTo.clientHeight;
var cellWidth = (appendToWidth-16)/cols;
var cellHeight = appendToHeight/rows;
var table = document.createElement('table');
for(var i=1; i<=rows; i++){
var row = document.createElement('tr');
for(var j=1; j<=cols; j++){
var cell = document.createElement('td');
cell.style.background = 'white';
cell.style.padding = '0px';
cell.style.margin = '0px';
cell.style.border = "1px solid blue";
cell.style.width = cellWidth+"px";
cell.style.height = cellHeight+"px";
row.appendChild(cell);
console.log("all ok");
}
table.appendChild(row);
}
table.style.borderCollapse = "collapse";
table.style.border = '1px';
appendTo.appendChild(table);
}
createGrid(15, 11, "target");
JSFiddle example
The real height of the table is: 21px * 20 cells + 21px(the borders) =441px. You have the value 21px for the borders because you added border-collapse: collapse; for the table. Without this, the height is 502px.
put the height to the row instead of the cell :
row.style.height = cellHeight+"px";
https://jsfiddle.net/4h83tu2j/17/
This is indeed related to the border adding to the size of your divs. I suggest you take a look at the css-property box-sizing, most particularly its border-box value. This adjusts the size related to the border you have set on it.
Adding it to your cell like so: cell.style['box-sizing'] = 'border-box'; and you will see a totally different result. The height of the cell should now take into account the border you have set on it. And thus make the cells fit the total height of your table.
You can find more information related to the border and the box-model here.
Related
I know the method .width() from jQuery returns the element's width without padding, border and margin.
In the accepted answer Table with vertical scroll, in which I can't comment, such method is used to get the width of the td elements of the first row of the table.
One of the jsFiddle in the answer there can be used to see the values returned by the method.
I tried to reproduce the behavior with this piece of code:
let colunas = document.querySelector('.scroll tbody tr:first-child').children;
let colunasWidth = [];
for (let i = 0, length = colunas.length; i < length; i++) {
colunasWidth.push(colunas[i].offsetWidth);//Width/clientWidth
}
I tried the various widths (offsetWidth, width, clientWidth), none gave the same result as jQuery and then I tried to get the border and padding width to subtract from such various widths, but I can't think of a way to get the math or right properties right.
Is there a simple and straightfoward way to do it?
You want window.getComputedStyle and .getPropertyValue
What it does is, it gets the styles used and then gets the actual width value of the element.
Here's a jsfiddle to show you: http://jsfiddle.net/u9d27wno/1/
var jquerywidth = $("#container").width();
var jqueryishwidth = window.getComputedStyle(document.getElementById("container"));
var offsetWidth = document.getElementById('container').offsetWidth;
var clientWidth = document.getElementById('container').clientWidth;
var msg = "offsetWidth: " + offsetWidth + "<br>\n";
msg += "clientWidth: " + clientWidth + "<br>\n";
msg += "jQuery width: " + jquerywidth + "<br>\n";
msg += "jQueryish width: " + jqueryishwidth.getPropertyValue("width") + "<br>\n";
document.getElementById('msg').innerHTML = msg;
//alert(document.getElementById('msg').innerHTML);
Let me know if that's the solution you needed!
You can use element.clientWidth and getComputedStyle together, to obtain teh value you are looking for...
element.clientWidth
The Element.clientWidth property is zero for elements with no CSS or inline layout boxes, otherwise it's the inner width of an element in pixels. It includes padding but not the vertical scrollbar (if present, if rendered), border or margin.
window.getComputedStyle
The window.getComputedStyle() method returns an object that reports the values of all CSS properties of an element after applying active stylesheets and resolving any basic computation those values may contain.
function width(el){
// get element computed styles
let styles=getComputedStyle(el);
// remove the 'px' from the returned values
let paddingLeft = styles['padding-left'].slice(0,-2);
let paddingRight= styles['padding-right'].slice(0,-2);
// substract paddings from value returned by clientWidth, and return value
return el.clientWidth - paddingLeft - paddingRight;
}
// test
let w = width(document.getElementById('test'))
console.log( 'VanillaJS:' , w )
console.log( 'JQuery : ', $('#test').width())
#test{
border:10px solid red;
width:200px;
margin:10px;
padding:10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="test">
I'm 200px + <br>
10px padding + <br>
10px border +<br>
10px margin
</div>
I need to calculate the text width without appending it to DOM.For this,i calculate and save each character's width i.e from (A-Z & a-z and other necessary characters).
Code :
function calculateCharactersWidth(){
for(var i=65;i<123;i++){
var x = $('<div style="float:left">').text(String.fromCharCode(i)).appendTo('#hiddenView');
charactersWidth[i] = x.width();
remHeight = x.height();
}
var x = $('<div style="float:left">').text(String.fromCharCode(45)).appendTo('#hiddenView');
charactersWidth[45] = x.width();
$('#hiddenView').empty();
}
and get each word's width like this
function getStringWidth(word){
var width=0;
for(var i=0;i<word.length;i++){
width=width+charactersWidth[word.charAt(i).charCodeAt(0)];
}
return width;
}
but when the word is appended to DOM. Its width differs from the calculated width.In some cases it differs by 3-4 px.
Even if we calculate it like this
// css font-weight:bold and font-size:14px
<div>Absin</div> width = 35.031
<div>A</div> width = 10.125
<div>b</div> width = 7.797
<div>s</div> width = 6.219
<div>i</div> width = 7
<div>n</div> width = 4.672
Added width = 35.831
This small difference creates a huge difference with array of words.So why is the difference and how can i make it work?
The kerning of the font is definitely messing up your calculation.
I am not sure why you'd need to do this, but why don't you print out your text in a hidden element and take the width of that?
function getStringWidth(word){
var x = $('<div style="float:left">').text(word).appendTo('#hiddenView');
return x.clientWidth;
}
This way you can also change the style, the font type and size, etc.
I applied this property in .css file on this table and records are 9.
.fixed tbody td, thead th {
width: 5.2%;
float: left;
}
Like when I have 4 column width must be 23.2% , when we have 5 columns width must be 18.2%, when I have 6 columns width must be 16.2% , when 7 width must be 14.2%. when columns are 8 width must be 12.2% . and maybe more columns come so I dont know but how can I manage this width ? I saw questions but they show that width must decrease or increase sequential. So what is best solution now ?
So you want to set equal width to each columns and don't know how many columns there is.
The width should then be the table width divided by the column amount.
$(document).ready(function(){
// The table to work on.
var table = $("table.fixed")
// Get table width
var tableWidth = table.width();
// Get the collection of the td on the first row.
var firstRowTdCollection = table.find("tr").first().find("td");
// Get the column count.
var columnCount = firstRowTdCollection.length;
// Calculate the width based on table width divided by the column amount.
var widthToApply = tableWidth / columnCount;
// Apply the calculated width on the first row (Not necessary on the other rows)
firstRowTdCollection.each(function(){
$(this).width(widthToApply);
});
});
Here is the infamous table:
My boss wants me, at this time of the year, to make the header of the table fixed, so the user can scroll down the table and continue reading the header. I want to preserve the original precomputed dimensions of the table, I mean, the width that every column has at the moment of its creation (widths aren't established by CSS) and then, adapt the header so its columns match the columns of the body. Following some of the answers I found in Stackoverflow, I started making the header and the body of the table display: block. And after that, I wrote this:
function setTableHeadDimensions() {
var $taskTable = $('.tablaTareas_PEMVISUALIZA'),
$firstRow = $taskTable.find('tbody > tr:first-child'),
$firstRowTds = $firstRow.find('td'),
$firstRowHead = $taskTable.find('thead > tr:first-child'),
$secondRowHead = $taskTable.find('thead > tr:eq(1)'),
$firstRowHeadThs = $firstRowHead.find('th'),
$secondRowHeadThs = $secondRowHead.find('th'),
i = 0,
cells = [];
//We prepare CSS, so we can specify width.
$taskTable
.css('table-layout', 'fixed')
.find('td, th').each(function () {
var $tdh = $(this);
$tdh.css('box-sizing', 'border-box');
$tdh.css('overflow', 'hidden');
});
//Cells of the first row of the table head.
for (i = 0; i < 3; i++) {
cells.push($($firstRowHeadThs[i]));
}
//Cells of the second row of the table head.
for (i = 0; i < $secondRowHeadThs.length; i++) {
cells.push($($secondRowHeadThs[i]));
}
//Rest of the cells for the first row.
for (i = 5; i < $firstRowHeadThs.length; i++) {
cells.push($($firstRowHeadThs[i]));
}
//Try to set the width of the current column's header cell
//to the biggest cell width in the column.
for (i = 0; i < cells.length; i++) {
var maxWidth = 0;
$taskTable.find('td:nth-child(' + (i + 1) + ')').each(function () {
var $el = $(this);
maxWidth = Math.max(maxWidth, $el.width());
});
cells[i].width(maxWidth);
}
}
But, as you can see in the picture, the browser doesn't want to cooperate. What's more, it establishes the width of the cell, but to a number that doesn't match the width of it's corresponding column:
What's more, it doesn't match the width of the row it should match:
So I have two questions:
Why does the browser behave in the way it does?
How can I solve this problem in a way compatible with IE8? (no fancy CSS3 solution, please)
Here's a codepen with the example cut down to the minimum necessary: Codepen example
I solved it. In reality, there were two problems:
The first one is that jQuery.width() returns only the width of the content of the cell, without padding and margin (even if you specify border-sizing: border-box). I found more natural to use jQuery.css('width') and then take borders and padding into account in my calculations, without specifying border-sizing: border-box because retrieving the width with border-sizing: border-box and then setting it in another element with the idea of matching both widths can be error prone (I had problems with it).
The second one is if you use rowspan in the header of the table. In that case, you have to establish the width of the rows envolved doing the proper calculations, not only one of them hopping that the rest of the rows will adapt.
Here's the codepen with the solution: http://codepen.io/PolarKuma/pen/BQXMbO
Given a fixed-header HTML table, I'm trying to line up the header columns with the body row columns. I'm doing this because the CSS I'm using to make the headers fixed results in the headers being out of alignment with the rest of the body.
The javascript I'm using works, but is extremely slow. Any ideas on how I can speed this up?
Here's the fiddle showing the problem. Right now it's taking about 5+ seconds for a relatively small table.
http://jsfiddle.net/w93NU/
Here is the code I'm using:
function fixedHeader($table) {
//This function compares the header row with the first body row and lines up all of the widths
var firstRowTds = $table.children("tbody:first").children("tr:first").children("td");
var headerRowThs = $table.find("th");
for (var i = 0; i < firstRowTds.length; i++) {
var head = headerRowThs[i];
var cell = firstRowTds[i];
var width = (Math.max($(cell).outerWidth(), $(head).outerWidth())) + "px";
//Here are my problem pieces. Setting these values are what kills the perfomanrce
$(cell).css({
"min-width": width,
"max-width": width
});
$(head).css({
"min-width": width,
"max-width": width
});
}
}
Ok, after much trial and error, if you comment out the last item in the style sheet, then it is fast. I don't know why.
Updated fiddle here
/* fixed width for THs */
/* the tbody needs to be 16px less than the thead, for the scrollbar */
/*#readiness-grid tbody td {
width: 242px;
}*/
// changing:
var firstRowTds = $table.children("tbody:first").children("tr:first").children("td");
var headerRowThs = $table.find("th");
// to:
var firstRowTds = $table.children("tbody:first > tr:first > td");
var headerRowThs = $table.find("thead > th");
scopes the node lookup and cuts the time from ~800ms to ~2ms between the start/end times on your sample table.