Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculating Paint Times of Elements In All Browsers

I am trying to calculate the elapsed time an element is painted onto the DOM from the start time of the script or if the specific element was even painted at all. I am inserting a background gradient to the HTML, and then using javascript to create random (clouds, which are just large periods with a text shadow) in multiple places across the screen (some negative, some positive, some within scope, some outside of scope).

Currently my code looks like this:

<html>
    <head>
        <style>
            .container {
                border: 1px solid #3b599e;
                overflow: hidden;
                filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#315d8c', endColorstr='#84aace'); /* for IE */
                background: -webkit-gradient(linear, left top, left bottom, from(#315d8c), to(#84aace)); /* for webkit browsers */
                background: -moz-linear-gradient(top,  #315d8c,  #84aace); /* for firefox 3.6+ */ 
            }
            .cloud {
                color: #fff;
                position: relative;
                font: 100% "Times New Roman", Times, serif;
                text-shadow: 0px 0px 10px #fff;
                line-height: 0;
            }
        </style>

        <script type="text/javascript">
            function cloud(){
                var b1 = "<div class=\"cloud\" style=\"font-size: ";
                var b2 = "px; position: absolute; top: ";
                document.write(b1+"300px; width: 300px; height: 300"+b2+"34px; left: 28px;\">.<\/div>");
                document.write(b1+"300px; width: 300px; height: 300"+b2+"46px; left: 10px;\">.<\/div>");
                document.write(b1+"300px; width: 300px; height: 300"+b2+"46px; left: 50px;\">.<\/div>");
                document.write(b1+"400px; width: 400px; height: 400"+b2+"24px; left: 20px;\">.<\/div>");
            }
            function clouds(){
                var top = ['-80','80','240','400'];
                var left = -10;
                var a1 = "<div style=\"position: relative; top: ";
                var a2 = "px; left: ";
                var a3 = "px;\"><script type=\"text/javascript\">cloud();<\/script><\/div>";
                for(i=0; i<8; i++)
                {
                    document.write(a1+top[0]+a2+left+a3);
                    document.write(a1+top[1]+a2+left+a3);
                    document.write(a1+top[2]+a2+left+a3);
                    document.write(a1+top[3]+a2+left+a3); 
                    if(i==4)
                    {
                        left = -90;
                        top = ['0','160','320','480'];
                    }
                    else 
                        left += 160;
                }
            }
        </script>
    </head>

    <body style="margin:0;padding:0;">
        <div class="container" style="width: 728px; height: 90px;">
            <script>clouds();</script>
        </div>
    </body>
</html>

I then run this inside of an iframe, trying to detect if the visible elements are being painted first, or if they are being painted in display order (pretty much, is the ad currently being viewed, or is it out of view).

I have not found a solid technique yet that works crossbrowser to detect this. In chrome, I was able to see it work when pasting images, as the visible images got an onload event fired first (even though they were at the end of the DOM), but this wasn't the case for firefox or IE.

like image 979
George Milonas Avatar asked Mar 21 '14 14:03

George Milonas


People also ask

How do browsers paint?

Browser computes the geometry of the layout and its elements based on the render tree. Browser paints pixel by pixel to create the visual representation we see on the screen.

What is DOM reflow?

Reflow occurs by doing the below-mentioned task: When you modify content on the page, e.g. the text in an input box. When you move a DOM element. When you animate a DOM element. When you take measurements of an element such as HTML DOM offsetHeight or JavaScript getComputedStyle() method. When you change a CSS style.

What is a repaint JavaScript?

parts of the screen will need to be updated, either because of changes in geometric properties of a node or because of stylistic change, such as changing the background color. This screen update is called a repaint, or a redraw.


2 Answers

I'm really not sure what you're after here. You said: "(pretty much, is the ad currently being viewed, or is it out of view)." But that is a bit cryptic.. What "ad" are you talking about?

In trying to figure out what you were trying to accomplish I pretty much rewrote your script to do exactly the same as before and it does an alert of the elapsed time as well as logs the time for each cloud in the console. It's not a good way to calculate execution time (which is what it seemed to me like you were after..).

NOTE: The only reason I rewrote everything was so I myself could make better sense of it in preparation for a response from you. Once I know exactly what you want I will edit my answer.

<style>
    .container {
        width: 728px;
        height: 90px;
        border: 1px solid #3b599e;
        overflow: hidden;
        filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#315d8c', endColorstr='#84aace'); /* for IE */
        background: -webkit-gradient(linear, left top, left bottom, from(#315d8c), to(#84aace)); /* for webkit browsers */
        background: -moz-linear-gradient(top,  #315d8c,  #84aace); /* for firefox 3.6+ */ 
    }
    .cloud {
        color: #fff;
        position: absolute;
        font: 100% "Times New Roman", Times, serif;
        text-shadow: 0px 0px 10px #fff;
        line-height: 0;
    }
    .cloud-container {
        position: relative;
    }
</style>

<div class="container"></div>

<script type="text/javascript">
// for IE
if (!window.console) window.console = {};
if (!window.console.log) window.console.log = function () { };

var pxTop  = ['34px', '46px', '46px', '24px'],
    pxLeft = ['28px', '10px', '50px', '20px'],
    size   = ['300px', '300px', '300px', '400px'];

function cloud(callback) {
    var df = document.createDocumentFragment(),
        node;
    for (var i = 0; i < 4; i++) {
        node = document.createElement('div');
        node.className = 'cloud';
        node.style.fontSize = size[i];
        node.style.width    = size[i];
        node.style.height   = size[i];
        node.style.top      = pxTop[i];
        node.style.left     = pxLeft[i];
        node.innerHTML      = '.';
        df.appendChild(node);
    }

    callback && callback();
    return df;
}

function clouds(containerId, callback) {
    var top       = ['-80','80','240','400'],
        left      = -10,
        container = document.querySelector(containerId);

    container.appendChild(document.createTextNode("\n"));

    for (i = 0; i < 8; i++) {
        var div        = document.createElement('div');
        div.className  = 'cloud-container';
        div.style.top  = top[0] + 'px';
        div.style.left = left + 'px';

        console.log('About to start making a cloud', new Date().getMilliseconds());
        div.appendChild(cloud(function() {
            console.log('Finished making cloud', new Date().getMilliseconds());
        }));
        container.appendChild(div);
        container.appendChild(document.createTextNode("\n")); // newline for view source

        if (i == 4) {
            left = -90;
            top  = ['0','160','320','480'];
        } else { 
            left += 160;
        }
    }
    callback && callback();
}

var start = new Date().getMilliseconds();
clouds('.container', (function() { 
    var end = new Date().getMilliseconds(),
       time = end - this;
    alert('Execution time: ' + time);
}).bind(start));
</script>
like image 108
Yes Barry Avatar answered Nov 04 '22 07:11

Yes Barry


You should use console API for measuring timings more accurately (and avoid DIY spaghetti):

console.time("Doing stuff");
/*le code*/
console.timeEnd("Doing stuff");

This functionality is available natively in Chrome and Firefox; it can also be polyfilled on other browsers like IE but will not be as precise.

Note that using Date is not appropriate/intended for benchmarking:

Depending on the browser and OS, Date's resolution can be as low as 15 milliseconds.

Date based on system time, isn't ideal. Most systems run a daemon which regularly synchronizes the time... tweaked a few milliseconds every 15-20 minutes. At that rate about 1% of 10 second intervals measured would be inaccurate.

Update:

Hold on, are you trying to measure browser paints' timing and order? These can't be monitored through js (ok, it's possible to have a js API, but ultimately it has to be benchmarked in the browser engine, so no js polyfills).

Rule of thumb is elements will be painted in parse order, with re-paints/re-flows triggered by content styling - like backgrounds being loaded, additional css rules becoming available or content of different size being added into a table column.

Html5rocks on profiling long paint times might get you started in the right direction.

like image 22
Oleg Avatar answered Nov 04 '22 09:11

Oleg