I have two tables, side by side. What I am trying to do is have each row of each table that is in the same container have the same row height per row. I have gotten that far with this.
The way it works is you have an array thay grabs the row heights of each table and uses the largest height for each row. Thats fine except as its a single array that means if there are other containers on the page they will look at the same array. I tried writting a closure function but failed. any ideas?
$(document).ready(function() {
var heights = [];
computeTableHeights(true);
assignTableHeights();
var windowWidth = $(window).width();
$(window).resize(function() {
computeTableHeights(($(window).width() < windowWidth) && ($(window).width() != windowWidth));
windowWidth = $(window).width();
assignTableHeights();
})
function computeTableHeights(recordBiggestHeights) {
$("table").each(function() {
var rowIndex = 0;
var rows = $(this).find("tr");
$(rows).each(function() {
var rowHeight = $(this).css("height");
if (heights[rowIndex] === undefined) {
heights[rowIndex] = rowHeight;
} else {
var existingHeight = parseInt(heights[rowIndex]);
var currentHeight = parseInt(rowHeight);
if (shouldChangeHeight(recordBiggestHeights, existingHeight, currentHeight)) {
heights[rowIndex] = rowHeight;
}
}
rowIndex++;
});
});
}
function shouldChangeHeight(recordBiggestHeights, existingHeight, currentHeight) {
if (existingHeight == currentHeight) {
return false;
} else if (recordBiggestHeights) {
return existingHeight < currentHeight;
} else {
return existingHeight > currentHeight;
}
}
function assignTableHeights() {
$(".container table").each(function() {
var rowIndex = 0;
var rows = $(this).find("tr");
$(rows).each(function() {
var rowHeight = $(this).css("height");
if (heights[rowIndex]) {
var existingHeight = parseInt(rowHeight);
var targetHeight = parseInt(heights[rowIndex]);
if (existingHeight != targetHeight) {
$(this).css("height", heights[rowIndex]);
}
}
rowIndex++;
});
});
}
});
I think I understand what you're trying to do. If not, please elaborate a little on the requirements you're looking for, so I can revise this answer.
You want to treat the row heights of each container and its child tables separately. Correct me if I'm wrong
The code below loops through each container separately, before equalizing the heights of the table rows.
You are indeed right that storing all row heights for all tables in one array will not get you the results you need. You would need to create an array instance per container.
In your code you read out the css for the height of the row. Once you set the height in the css, this property will stay the same. I believe in your use-case you need the height of the row as the browser has calculated it (jquery offers methods for this purpose).
Therefore, on resizing, the css property should be cleared, before setting it again to the greatest calculated height.
function resizeHandler() {
// Treat each container separately
$(".container").each(function(i, container) {
// Stores the highest rowheight for all tables in this container, per row
var aRowHeights = [];
// Loop through the tables
$(container).find("table").each(function(indx, table) {
// Loop through the rows of current table (clear their css height value)
$(table).find("tr").css("height", "").each(function(i, tr) {
// If there is already a row height defined
if (aRowHeights[i])
// Replace value with height of current row if current row is higher.
aRowHeights[i] = Math.max(aRowHeights[i], $(tr).height());
else
// Else set it to the height of the current row
aRowHeights[i] = $(tr).height();
});
});
// Loop through the tables in this container separately again
$(container).find("table").each(function(i, table) {
// Set the height of each row to the stored greatest height.
$(table).find("tr").each(function(i, tr) {
$(tr).css("height", aRowHeights[i]);
});
});
});
}
$(document).ready(resizeHandler);
$(window).resize(resizeHandler);
I have this fiddle: http://jsfiddle.net/k5g87/
You can resize the result window in there.
Related
I have a table with dynamic content.
When it loads I want a certain row (it differs which one) to be at the top.
This is doable in cases where the table has enough rows below the one I want to scroll into view.
But when I want for instance the 12th row at the top and the table only has 14 rows. It will not scroll down far enough because there is nothing to fill up the remaining space.
I'm guessing I need something to fill the left over space. But something that should shrink to 0 if the table is in a scrollposition that it already fills its container.
I could get an element to fill up that space. But when the user scrolls up it should reduce in height (as the table contents move down) and not grow after that. Meaning the user can scroll up to the top just once.
Someone asked: "Did you try to write something yourself?"
Well, I've come up with this. It almost works on page load, then when you navigate from that to a different anchor, the spacer becomes way too large. For some reason much larger than on the first page load.
Scrollreference is the element that the table scrolls within.
var createSpacer = function (row, height) {
// add element with height equal to distance
spacer = document.createElement('div');
var container = getClosestBySelector(row, tableContainerSelector);
container.appendChild(spacer);
spacer.className = spacerClass;
spacer.style.height = height + 'px';
return spacer;
};
var getInitialSpacerHeight = function (row) {
var scrollreference = getClosestBySelector(row, scrollReferenceSelector);
// get scrollreference that has this row
// return distance from top of table
var sizeAboveRow = row.getBoundingClientRect().top - scrollreference.getBoundingClientRect().top - window.scrollY;
return sizeAboveRow;
};
var navigationHandler = function (e) {
// set anchored row to top even if table doesn't have enough rows to put it there
var rowID = e.detail.newLocation.substring(e.detail.newLocation.indexOf("#") + 1);
// only if the new location has an anchor
if (rowID !== "") {
var row = document.querySelector('#' + rowID);
var spacerHeight = getInitialSpacerHeight(row);
if (spacerHeight === 0) {
// if spacer height = 0 we don't need to act
// remove listener and/or spacer here?
return;
} else {
createSpacer(row, spacerHeight);
var scrollreference = getClosestBySelector(row, scrollReferenceSelector);
scrollreference.scrollTop = scrollreference.scrollHeight;
}
}
}
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.
I found this sweet jQuery snippet by CSS-Tricks' Chris Coyier that resets div elements heights that share the same top position on the page (are on the same row) to the tallest element.
The Problem
This solution almost works with fluid width layouts and resets height when top positions changes but it resets it to the original height of the current tallest element in the row when the page first loaded. This is an issue because the height of the tallest element might have changed since this page first loaded because of the use of relative units like ems or because of word wrapping with paragraphs.
Proposed Solution
The solution would be to have the row's elements' height being set to the tallest element's current height, not the original height. I have been unsuccessful in accomplishing this.
Here is the snippet where "li.half" is the elements being compared and resized.
jQuery(document).ready(function($) {
// these are (ruh-roh) globals. You could wrap in an
// immediately-Invoked Function Expression (IIFE) if you wanted to...
var currentTallest = 0,
currentRowStart = 0,
rowDivs = new Array();
function setConformingHeight(el, newHeight) {
// set the height to something new, but remember the original height in case things change
el.data("originalHeight", (el.data("originalHeight") == undefined) ? (el.height()) : (el.data("originalHeight")));
el.height(newHeight);
}
function getOriginalHeight(el) {
// if the height has changed, send the originalHeight
return (el.data("originalHeight") == undefined) ? (el.height()) : (el.data("originalHeight"));
}
function columnConform() {
// find the tallest DIV in the row, and set the heights of all of the DIVs to match it.
$('li.half').each(function() {
// "caching"
var $el = $(this);
var topPosition = $el.position().top;
if (currentRowStart != topPosition) {
// we just came to a new row. Set all the heights on the completed row
for(currentDiv = 0 ; currentDiv < rowDivs.length ; currentDiv++) setConformingHeight(rowDivs[currentDiv], currentTallest);
// set the variables for the new row
rowDivs.length = 0; // empty the array
currentRowStart = topPosition;
currentTallest = getOriginalHeight($el);
rowDivs.push($el);
} else {
// another div on the current row. Add it to the list and check if it's taller
rowDivs.push($el);
currentTallest = (currentTallest < getOriginalHeight($el)) ? (getOriginalHeight($el)) : (currentTallest);
}
// do the last row
for (currentDiv = 0 ; currentDiv < rowDivs.length ; currentDiv++) setConformingHeight(rowDivs[currentDiv], currentTallest);
});
}
$(window).resize(function(){
columnConform();
});
// Dom Ready
// You might also want to wait until window.onload if images are the things that
// are unequalizing the blocks
$(function() {
columnConform();
});
});
Please let me know if you can figure out how to make the setConformingHeight adjust on window resize.
Those solutions didn't work on window.resize() as elements height should be unlocked with $el.height('auto') before calculating new real height.
Here is my solution :
var currentRowTop = -100, currentHighest= 0;
$('.page-wrapper .cc').each(function() {
$el=$(this);
if($el.position().top!=currentRowTop){
equalizeHeight();
currentRowTop = $el.position().top;
$el.height('auto').addClass('same-height');
currentHighest=$el.height();
}else{
$el.height('auto').addClass('same-height');
currentHighest = ($el.height()>currentHighest) ? $el.height() : currentHighest ;
}
});
equalizeHeight();
function equalizeHeight(){
if($('.same-height').size()==0) return;
$('.same-height').height(currentHighest).removeClass('same-height');
}
I have a piece of code where I am trying to dynamically change the heights of children elements and I am running into something bizarre. Here is my code so far:
var heights = [],
tallest;
$(window).resize(function() {
$(elem).children().each(function (i) {
heights[i] = $(this).height();
tallest = Math.max.apply(null, heights);
$(this).height(tallest);
});
});
All this does is find the heights of each child element, pushes it into an array and then finds the biggest value in that array and attempts to set the height based on that value. One would expect that each child element that is found will change according to what the tallest variable value is on resize, but it doesn't -- it stays the same. However, this code works:
var heights = [],
number = 1000;
$(window).resize(function() {
$(elem).children().each(function (i) {
heights[i] = $(this).height();
tallest = Math.max.apply(null, heights);
$(this).height(number--);
});
});
The height attribute is changed inline with the number variable when resizing the window. What gives?
Why does the second piece of code work and the first one doesn't? They look like almost the same code to me.
It won't assign the max value right away because you're not really calculating the Max height. You have to let it run for all the child elements to get the tallest value. And then run again to assign to each child.
var heights = [],
tallest;
$(window).resize(function() {
$(elem).children().each(function (i) {
heights[i] = $(this).height();
});
tallest = Math.max.apply(null, heights);
$(elem).children().each(function (i) {
$(this).height(tallest);
});
});
Figured it out. You have to reset the height every time in the original loop so it pushes a new value into the array.
$(window).on("resize", function () {
$(elem).children().each(function (i) {
$(this).css("height", "");
heights[i] = $(this).height();
});
});
Solved it for me.
The issue comes when you set the height in the other each, it sets the biggest value. You need to get rid of the inline height each time to retrieve the new value.
Hey there, I have 20 divs floated left with different height. I use this script to resize them. It worked perfect when my website was designed using pixels.
When i have changed my website to % design (percentage design), the script stopped working that reliable, sometimes it does not resize.
can you take a look, see if there are any adjustments needed for liquid layouts?
maybe it's the way i call the script?
Ty very much
Here it is:
var currentTallest = 0;
var currentRowStart = 0;
var rowDivs = new Array();
function setConformingHeight(el, newHeight) {
// set the height to something new, but remember the original height in case things change
el.data("originalHeight", (el.data("originalHeight") == undefined) ? (el.height()) : (el.data("originalHeight")));
el.height(newHeight);
}
function getOriginalHeight(el) {
// if the height has changed, send the originalHeight
return (el.data("originalHeight") == undefined) ? (el.height()) : (el.data("originalHeight"));
}
function columnConform() {
// find the tallest DIV in the row, and set the heights of all of the DIVs to match it.
$('div.column').each(function(index) {
if(currentRowStart != $(this).position().top) {
// we just came to a new row. Set all the heights on the completed row
for(currentDiv = 0 ; currentDiv < rowDivs.length ; currentDiv++) setConformingHeight(rowDivs[currentDiv], currentTallest);
// set the variables for the new row
rowDivs.length = 0; // empty the array
currentRowStart = $(this).position().top;
currentTallest = getOriginalHeight($(this));
rowDivs.push($(this));
} else {
// another div on the current row. Add it to the list and check if it's taller
rowDivs.push($(this));
currentTallest = (currentTallest < getOriginalHeight($(this))) ? (getOriginalHeight($(this))) : (currentTallest);
}
// do the last row
for(currentDiv = 0 ; currentDiv < rowDivs.length ; currentDiv++) setConformingHeight(rowDivs[currentDiv], currentTallest);
});
}
$(window).resize(function() {
columnConform();
});
$(document).ready(function() {
columnConform();
});
Well if you change it to fluid layout (% design) then you are going to have to add a window resize listener, basically when the resize event is done or while it's running you need to recall the script so it can recalculate with new dimensions, you did not need to doo that with pixels because it was a fixed size and once assigned will not change no matter how many times you resize the actual screen.
If you use styles like this:
<style>
.parent{
background:#F00;height:300px
}
.parent div{
float:left;height:100%;width:33.33%;
}
</style>
<div class="parent">
<div style="background:#FED"></div>
<div style="background:#EDF"></div>
<div style="background:#DFE"></div>
</div>
You just have to set the height of the parent div, and the width of the children div