A very, very, very big div

JavascriptHtmlCssGoogle Chrome

Javascript Problem Overview


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>

Javascript Solutions


Solution 1 - Javascript

Changing to position: fixed seems to speed things up.

Solution 2 - Javascript

Use transform instead of top/left:

container.style.transform = 'translate(' + container.x + 'px, ' + container.y + 'px)';

A live demo at jsFiddle.

Solution 3 - Javascript

  1. 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.

  1. 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

Solution 4 - Javascript

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.

Solution 5 - Javascript

@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.

Solution 6 - Javascript

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.

Solution 7 - Javascript

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBasjView Question on Stackoverflow
Solution 1 - Javascriptgeert3View Answer on Stackoverflow
Solution 2 - JavascriptTeemuView Answer on Stackoverflow
Solution 3 - JavascriptViliusLView Answer on Stackoverflow
Solution 4 - JavascriptPrisonerView Answer on Stackoverflow
Solution 5 - JavascriptKoen.View Answer on Stackoverflow
Solution 6 - JavascriptTheodore FerroView Answer on Stackoverflow
Solution 7 - JavascriptPaul SweatteView Answer on Stackoverflow