For a project of mine (see BigPictu.re or bigpicture.js GitHub project), I have to deal with potentially a very, very, very big <div> container.
I knew there was a risk of poor performance with the simple approach I use, but I did not expect it to be mostly present with ... Chrome only!
If you test this small page (see code below), panning (click + drag) will be:
Normal / smooth on Firefox
Normal / smooth even on Internet Explorer
Very slow (nearly crashing) on Chrome!
Of course, I could add some code (in my project) to do that when you're zoomed in a lot, text with potentially very very big font-size would be hidden. But still, why does Firefox and Internet Explorer handle it correctly and not Chrome?
Is there a way in JavaScript, HTML, or CSS to tell the browser not to try to render the whole page (which is 10000 pixels wide here) for every action? (only render the current viewport!)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style>
html, body {
overflow: hidden;
min-height: 100%; }
#container {
position: absolute;
min-height: 100%;
min-width: 100%; }
.text {
font-family: "Arial";
position: absolute;
}
</style>
</head>
<body>
<div id="container">
<div class="text" style="font-size: 600px; left:100px; top:100px">Small text</div>
<div class="text" style="font-size: 600000px; left:10000px; top:10000px">Very big text</div>
</div>
<script>
var container = document.getElementById('container'), dragging = false, previousmouse;
container.x = 0; container.y = 0;
window.onmousedown = function(e) { dragging = true; previousmouse = {x: e.pageX, y: e.pageY}; }
window.onmouseup = function() { dragging = false; }
window.ondragstart = function(e) { e.preventDefault(); }
window.onmousemove = function(e) {
if (dragging) {
container.x += e.pageX - previousmouse.x; container.y += e.pageY - previousmouse.y;
container.style.left = container.x + 'px'; container.style.top = container.y + 'px';
previousmouse = {x: e.pageX, y: e.pageY};
}
}
</script>
</body>
</html>
Changing to position: fixed seems to speed things up.
Use transform instead of top/left:
container.style.transform = 'translate(' + container.x + 'px, ' + container.y + 'px)';
A live demo at jsFiddle.
Answer to first quest "why". One of problems are font size. you have font size 600000px, most browser will see it as too high and render smaller, while chrome tries to render original size. Looks like chrome can not repaint such big letters with your requested styles very fast.
But combining Teemu and geert3 answers - using transform and position:fixed, makes chrome works much more faster even with big fonts.
Answer to 2nd question: "Is there a way ... not to try to render the whole page" - you can try to apply mouse action for elements in container, not for whole container.
Maximum font sizes: http://jsfiddle.net/74w7yL0a/
firefox 34 - 2 000 px
chrome 39 - 1 000 000 px
safari 8 - 1 000 000 px
ie 8-11 - 1 431 700 px
In addition to Teemu's answer of using translate:
container.style.transform = 'translate(' + container.x + 'px, ' + container.y + 'px)';
Which you should also use other vendor prefixes, You can simply fix this by using this on the body:
height: 100%;
width: 100%;
position: relative;
overflow: hidden;
and this on html:
height: 100%;
this will, however, disable scrolling. So what I'd do is, add a mousedown event to the body and apply those styles using a css class whenever mousedown is triggered, and removing that class on mouseup.
#Teemus' answer almost does it all.
Use transform with translate3d instead of top/left.
translate3d enables hardware acceleration.
container.style.transform = 'translate3d(' + container.x + 'px, ' + container.y + 'px, 0)';
A live demo at jsFiddle.
I analyzed this and I found that the original problem related to the Chrome display architecture, and its use of background threads to render the page.
If you want to have fast rendering, go into chrome:flags, scroll to the setting Impl-side painting, and set "Disabled", then restart the browser - the mousemove will be smooth.
What I found is that if you enable the FPS counter, the reported FPS in this scenario is still very high, even though the actual onscreen performance is very low. My tentative explanation (not being a Chrome display architecture expert) is that if the UI thread and display are on separate threads, then there can be contention in the rendering of the div - in the case where the UI thread and rendering thread is on the same thread, the UI thread cannot send messages faster than the UI thread can render.
I would suggest that this should be filed as a Chrome bug.
Use display: table and table-layout:fixed on the div, or a table wrapping the div. In HTML:
The HTML table model has been designed so that, with author assistance, user agents may render tables incrementally (i.e., as table rows arrive) rather than having to wait for all the data before beginning to render.
In order for a user agent to format a table in one pass, authors must tell the user agent:
The number of columns in the table. Please consult the section on calculating the number of columns in a table for details on how to supply this information.
The widths of these columns. Please consult the section on calculating the width of columns for details on how to supply this information.
More precisely, a user agent may render a table in a single pass when the column widths are specified using a combination of COLGROUP and COL elements. If any of the columns are specified in relative or percentage terms (see the section on calculating the width of columns), authors must also specify the width of the table itself.
For incremental display, the browser needs the number of columns and their widths. The default width of the table is the current window size (width="100%"). This can be altered by setting the width attribute of the TABLE element. By default, all columns have the same width, but you can specify column widths with one or more COL elements before the table data starts.
The remaining issue is the number of columns. Some people have suggested waiting until the first row of the table has been received, but this could take a long time if the cells have a lot of content. On the whole it makes more sense, when incremental display is desired, to get authors to explicitly specify the number of columns in the TABLE element.
Authors still need a way of telling user agents whether to use incremental display or to size the table automatically to fit the cell contents. In the two pass auto-sizing mode, the number of columns is determined by the first pass. In the incremental mode, the number of columns must be stated up front (with COL or COLGROUP elements).
and CSS:
17.5.2.1 Fixed table layout
With this (fast) algorithm, the horizontal layout of the table does not depend on the contents of the cells; it only depends on the table's width, the width of the columns, and borders or cell spacing.
The table's width may be specified explicitly with the 'width' property. A value of 'auto' (for both 'display: table' and 'display: inline-table') means use the automatic table layout algorithm. However, if the table is a block-level table ('display: table') in normal flow, a UA may (but does not have to) use the algorithm of 10.3.3 to compute a width and apply fixed table layout even if the specified width is 'auto'.
References
HTML 4: 11.2, Elements for constructing tables
HTML 4: Appendix B, Section 5.1.2: Incremental Display
HTML 5: Algorithm for Processing Rows
CSS2: 17.5.2 Table width algorithms, the 'table-layout' property
Draft U.S. Web Design Standards Documentation | Tables
Related
I am trying to implement a sticky header row for table, but need to do it without position: sticky property.
The approach I am going for is translating Y coordinate of header row along with document vertical scroll by same amount of pixels.
As the document is scrolled down, we translate the header row downwards by same number of pixels giving the effect that header row is stuck at the top of table.
I have a working implementation at https://codepen.io/shubham_687/pen/porPwbP
PFA the gist of implementation below
canvas.onscroll = function (e) {
if (canvas.scrollTop >= rect.top) {
const numOfPixels = canvas.scrollTop - rect.top;
element.setAttribute(
"style",
"transition:0s;transform: translate3d(0px," + numOfPixels + "px, 0px);"
);
} else {
element.setAttribute(
"style",
"transition:0s;transform: translate3d(0px, 0px, 0px);"
);
}
};
The issue I am facing is that the implementation is jerky/sluggish/jumpy in some browsers and mobile devices while working perfectly fine in some (Google chrome).
As per my understanding, this behavior might be because of the following reason(s)
Large number of scroll events per second causing perf impact, might be possible to solve using debouncing, any suggestions?
Updating css dynamically leads to update layer tree which takes some time as well and the number of times this gets called is directly proportional to the times scroll event is registered.
Please share possible suggestions for perf improvement.
Demo
I am working on a table whose columns can be resized by dragging on the right side of any header element. In addition, the table is contained within a class grid, which is both draggable and resizable.
As shown in the demo, the actual functionality of these features work just fine. However, in Firefox, resizing columns and resizing the grid vertically cause significant performance lag. In fact, even IE has better performance for resizing the columns, although it still has lag on the grid vertical resize. That leaves Chrome, which doesn't have any performance issues whatsoever.
What can I do to make these resize actions responsive across all browsers and scalable for larger row numbers (say, 150 instead of the 50 in the fiddle)?
I suspect the issue lies in the alsoResize option for each header:
$('#header-D').resizable(
{
handles: "e",
minWidth: 20,
alsoResize: "td:nth-child(5)"
}
);
I'm thinking the nth-child selector should be replaced with $("td").eq(5), although I can't find how to make that co-operate with JSON notation. Or perhaps there's a way to resize one td that automatically resizes all the corresponding td's below, but I'm not sure how this is feasible in this particular instance since I've been forced to use display:inline-block on the cells to make the table formatting work with the scrollbar and resizing functionality.
You are experiencing a lag while resizing because you are executing a large block of code that modifies the DOM every moment the grid is re-sized.
Modifying the DOM is an expensive operation and should be used sparingly. Every time the DOM is modified, the browser must re-draw the page to reflect your changes. There are definitely browser differences in how well they handle re-drawing the page.
Frankly, I'm very impressed that Chrome is able to handle all of your DOM modifications without a discernible lag. There is a lot of DOM modifying code in your resize handler function.
To fix this problem, you should only modify the DOM once the user has finished re-sizing the element. You can do this with a setTimeout and an extra pair of width and height variables. If the width and height variables from outside the setTimeout match the values retrieved inside the setTimeout, then you know that the user has stopped resizing the grid and you can safely modify the DOM.
Working JSFiddle
The significant changes in the above JSFiddle are:
The resize function handler:
var rGridNewWidth = parseInt($('.grid').css("width"));
var rGridNewHeight = parseInt($('.grid').css("height"));
setTimeout(function() {
var gridNewWidth = parseInt($('.grid').css("width"));
var gridNewHeight = parseInt($('.grid').css("height"));
if (rGridNewWidth == gridNewWidth && rGridNewHeight == gridNewHeight) {
$('.grid .container').css("height", gridFeedManagerContainerHeight + (gridNewHeight-gridInitialHeight));
$('.grid .grid-table-container').css("width", gridTableWidth + (gridNewWidth-gridInitialWidth));
$('.grid .grid-table-container').css("height", gridTableHeight + (gridNewHeight-gridInitialHeight));
$(".grid-snap-outer").css("width", parseInt($(".grid").css("width")));
$(".grid-snap-outer").css("height", parseInt($(".grid").css("height")));
$(".button-ok").css("width",gridTableWidth + (gridNewWidth-gridInitialWidth));
$('.grid .grid-table-header-container').css("width", gridTableHeaderWidth + (gridNewWidth-gridInitialWidth));
$(".corner-right").css("left", cornerOffset + (gridNewWidth-gridInitialWidth));
}
}, 150);
Setting the grid overflow to hidden:
overflow:hidden;
In my example, I set the delay on the setTimeout to 150 milliseconds. This means that if the user stops resizing the grid for 150 milliseconds, then the grid will resize.
I have a problem with height limitation of <div></div> in some web browsers, like Firefox. I have javascript code like this:
$('#MyDiv').css("height","20000000px"); // 20,000,000 pixel height
But I see height: 2e+7px; rule in the firebug. This problem exist in IE too, But in google chrome everything is ok and I see height: 20000000px;. How can I set very large value to div's height in a way that works for most browsers?
EDIT: firefox has no scrollbar in this div, but google chrome has scrollbar.
I want just confirm the problem which describes hamed. One can try the demo http://jsfiddle.net/OlegKi/y4tLxx53/4/ which contains setting height property using jQuery.css on the test div:
var testValues = [10000, 1533916, 1533917, 1533918, 10737418, 10737419,
17895696, 17895697, 17895698, 20000000], h, i;
for (i = 0; i < testValues.length; i++) {
h = testValues[i] + "px";
$("#test").css("height", h);
$("#log").append("<span>After setting height " + h +
", one have height: " + $("#test").css("height") +
"</span><br/>");
}
with very simple HTML markup
<div id="log"></div>
<div id="test"></div>
One can see in Google Chrome the expected results
but Firefox shows
and IE10 and IE11 displays
instead.
By the way, the setting of large height on divs will be used to implement "virtual scrolling" (for example in jqGrid). So that the user sees div with large scroll and a table inside. If the user uses scroll bar then the page of data will be downloaded from the server via Ajax. In the way the height of the div should corresponds to the size of data on the server. If one row of table data have height 23px, then IE10/IE11 can simulate in the simple way only 66692 rows of virtual data in IE (1533916/23=66692) and 778073 rows (less as a million rows) in Firefox. The demos shows that one need use more sophisticated implementation of "virtual scrolling" to have no, described above, problems with setting of height of div.
One can use the same inline demo alternatively:
var testValues = [10000, 1533916, 1533917, 1533918, 10737418, 10737419,
17895696, 17895697, 17895698, 20000000], h, i;
for (i = 0; i < testValues.length; i++) {
h = testValues[i] + "px";
$("#test").css("height", h);
$("#log").append("<span>After setting height " + h +
", one have height: " + $("#test").css("height") +
"</span><br/>");
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<div id="log"></div>
<div id="test"></div>
I have run into the same issue implementing virtual scrolling. My solution is to detect numbers that are too large and use a multiplier.
This means that the height of the overflowed content will be realHeight / multiplier. And when I'm trying to figure out what data to show, I just take the scrollTop * multiplier. This works fine to the user because the scrollbar has a minimum height.
This will work if you have control over the effect. I'm not familiar with jqGrid, but the source code may have to be updated.
That's not a problem. The value 2e+7 is the same as 20000000, it's just a different way of showing the number.
In some tools, large numbers are shown in scientific notation. The number 2e+7 means 2 * 107.
If your document is size is not fixed, you can use this:
var height = $(document).height();
and set your div container height accordingly
$('#MyDiv').css("height",height);
This should work on all the browsers
I am working on a document with the structure like this:
<html>
<head>
<script src="http:http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<h1>Title</h1>
<div id="main">
<div id="head">Some text</div>
<div id="content"><canvas id="canvas"></canvas></div>
<div id="foot">Some more text (and a table)</div>
</div>
</body>
</html>
This is, of course, a bit simplified, but shows the real structure of the document.
Now, I want to extend #canvas so that my document is as wide and as high as possible without anything getting out of the viewport (i.e., without getting scroll bars on my document).
CSS-only solutions are not O.K. for at least two reasons:
This is done dynamically, on request. Depending on some other factors, user may chose the content of #canvas to be bigger or smaller than that. What I want is a "Make optimal size" button (I know how to make the button itself -- this is only about determining the sizes).
The canvas element will not end up with the determined size, but slightly changed, to be a multiplier of some parameter, i.e., if height is the maximal allowable height we compute, the canvas will get its height actually set to factor * Math.floor(height / factor).
I have managed to get the proper width using jQuery:
canvas.width = 0;
canvas.height = 0;
var t = $(canvas);
var w = $(window).width() - (t.outerWidth(true) - t.width());
$(canvas).parents().map(function() {
t = $(this);
w -= t.outerWidth(true) - t.width();
});
This simply takes the width of the viewport and chips away borders, paddings and margins of #canvas and all its parents, after setting canvas to size 0x0 (to prevent miscalculations if it is currently to wide and the document already has scrollbars). This works as intended, however, I've been unsuccessful in doing something similar for the height (and making it work and all modern browsers).
Among other things, I have tried using elements' position().top (which seems to behave differently in SeaMonkey, Chrome and Firefox) and I've tried playing with $(window)/$(document)/other elements' height(), innerHeight() and outerHeight(), but none of these worked.
Edit:
Maybe an additional explanation would help. The canvas is a gameboard. Think chess (although its not chess). So, the user can chose to play on a grid of mxn tiles (m,n being integers chosen via jQueryUI spins; each tile is factor wide and high). So, I want to give the user a button which would essentially say "make the board as big as possible without me needing to scroll while playing".
Edit (a possible solution)
This seems to do the trick:
h = window.innerHeight - $(main).position().top - $(main).outerHeight(true);
I've tested it under SeaMonkey (where $(window).height() was not working as expected), Firefox and Chrome. I have no way of testing IE newer than IE8 (where, as I expected, canvas itself doesn't work).
Please don't consider my question as a duplicate. I just din't succeed trying Display divs with different sizes with CSS
As suggested in the above post i used Masonry. But failed to get it worked. I am using codeigniter.
Here are the css i am using
#container {
width:90%;
margin:3% 0px 0px 10%;
}
.item {
margin:10px 10px 10px 10px;
float:left;
width:240px;
border:5px solid #f0f0f0;
background-color:#d2dbde;
border-radius:5px;
}
Javascript and js files
<!-- include js files -->
<script type="text/javascript" src="http://www.myappdemo.com/KarmakExpo/js/jquery.min.js"></script>
<script type="text/javascript" src="http://www.myappdemo.com/KarmakExpo/js/jquery.masonry.min.js"></script>
<script type="text/javascript">
$(function() {
$('#container').masonry({
// options
itemSelector : '.item'
});
});
</script>
content
<div id="container">
<div class="item">
<div id="usericon" style="width:240px;height:30px;">
<!-- content -->
</div>
<div id="name">
<!-- content -->
</div>
<div>
<a href="<?php echo $link; ?>">
<img src="<?php echo $picture = ($picture == null) ? '' : $picture; ?>" width="240px" height="auto">
</a>
</div>
I am displaying images,name,date etc in div section
Dynamic divs put in their place
JsFiddle - Demo (number of columns depends on width of document window).
Since it appears you have divs of regular widths, you might try something like this:
Note: Since first answering with this simple demo script, I have substantially altered the linked jsFiddle demo. It now barely resembles this code, but the basics are pretty much the same.
CSS kinda like this
div.column {
display:inline-block; /* "Columns" should be only as wide as their setting, */
vertical-align:top; /* should sit next to each other horizontally, */
width:240px; /* and be vertically aligned. */
height:auto;
margin:10px 0px 10px 10px;
}
div.column div.row {
width:240px; /* All "row" divs are of the same width, */
height:auto; /* but of varying heights */
margin:0px 0px 10px 0px;
padding:0px;
background-color:#00f0f0;
}
JavaScript kinda like this
(function () {
var tdw = 240 + 0 + 10; // Div width + margin-left + margin-right
window.addEventListener("load", function () {
var ww = window.innerWidth, // how much width to play with?
cn = Math.floor(ww / tdw), // how many columns will fit?
cdl = [], // memory
lc = 0, // alternation
c = 0, // iteration
ncd; // element object
while (c++ < cn) {
ncd = document.createElement("div"); // create columns
ncd.setAttribute("class", "column"); // set their class
document.body.appendChild(ncd); // add to page
cdl.push(ncd); // remember them
}
c = 0;
while (c++ < 100) { // this for demo // loop until there're no more
ncd = document.createElement("div"); // create your divs
// this could be where you add your div content
ncd.setAttribute("class", "row"); // set their class
lc = lc < cdl.length ? lc : 0; // alternate column as parent
cdl[lc++].appendChild(ncd); // add the div to the column
ncd.style.height = (200 * Math.random()) + "px"; // this for demo
// or you could add the content via innerHTML
}
}, false);
}());
This answer was put together whilst assuming a lot. With more detail in the question, I could have provided a more complete answer.
Since being asked to explain...
As I understand the question, it is to find a way to take dynamic information (extracted from where is irrelevant), and fill divs with it. Each of those divs is to be set on the page (presumably within a "feed" container or similar) in columns. Since the width of these (lets call them "infodivs") infodivs is of a set width, we can create columns of fixed widths to contain them. Now the divs are free to be whatever height they need to be (according to the info they contain), and will simply stack up on top of each other, within their parent div.column.
On page load we measure the available width (in a live version accounting for offsets etc), and calculate how many columns will fit horizontally, then create those columns. To save reading and re-reading the DOM, we can store the columns to an array for easy look-up later.
After creating the columns, we are free to add the dynamically created infodivs to the columns, cycling through the column look-up array, utilizing each progressive column (left to right across the screen) for each new infodiv. Once we get to the last column, we set the look-up counter back to zero, and continue loading infodivs.
The method results in each column being filled with an approximately equal number of info divs (dependant on maths). There is however no check of the height of each infodiv, so any column could end up with much longer content than the others. A way around this would be to measure the height of each column as each new infodiv is created, then add that infodiv to the column which is shortest. This would result in columns remaining more closely equal in height.
Note: The demonstration jsFiddle connected to this answer now contains a rudimentary function to dynamically measure the column heights as infodivs are being created. In order to get an accurate reading of the column height(s), each image has a temporary onload listener attached which triggers the creation of the next infodiv. The listener is removed as soon as it's done it's job to free up resources. This method slows overall page loading, but not enough to be impractical. Depending on real circumstances, faster less accurate loading might be more desirable. In that case, discard the image's onload listeners and create infodivs on demand regardless of the state of those previously created.
Further to dynamic measurement: The loading of large amounts of data and/or images (especially images) could be improved by the addition of an onscroll listener triggering a function to load new data (infodivs in this case) only when the visitor is scrolling toward the end of what they already see. This would serve to reduce server stress and increase browser response. Plus: there's no point loading what the visitor may never scroll to look at.
So the code in pseudo terms is something like:
How wide is the screen?
Make (screen-width divided by column-width) columns.
While we have new infodivs being created, add them to the columns.
Don't add them all to one column, but shared them out equally.
The end result is dynamically created divs of info of equal widths, but varying heights, being laid out in a columnized fashion. Their natural tendency is to be as high up their parent as possible, so they'll always be sitting just beneath the infodiv above them.
Since the columns have their display property set to inline, they'll tend to sit side by side where there is space for them. A caveat is that if the width of the column's parent is reduced (after the initial layout is created), the right-most column will be pushed below its fellow columns.
As for the PHP - That's another story :-)