14

I am encountering some strange behavior for the following code.

    function linkFunc(scope, element, attribute) {
        var page = angular.element($window);

        page.bind('scroll', function() {

            var windowHeight = "innerHeight" in window ? window.innerHeight : document.documentElement.offsetHeight;
            var body = document.body, html = document.documentElement;
            var docHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight,  html.scrollHeight, html.offsetHeight);
            var windowBottom = windowHeight + window.pageYOffset;

            if (windowBottom >= docHeight) {
                scope.$apply(attribute.myDirective);
            }
        });
    }

The above is a piece of code that detects if the bottom of the page is reached, if its reached it will call whatever function bind to myDirective

The main issue is that most of the time the lazy loading works, and myDirective gets sucessfully called. However some of the times the lazy loading won't work, and I wasn't able to reproduce the bug.

I tried different screen size, different browser, but it seems like the bug just happens randomly.

Maybe someone have this happened to them before, and can point me a direction?

Edit:

More information

I was able to reproduce the bug after a bit of experimenting.

Basically, when the zoom in percentage of the browser is < 100 % , window.pageY returns a decimal value that is slightly inaccurate which cause windowBottom to be off by a 0.1 to 0.9

eg.

            console.log(windowBottom); // 1646.7747712336175
            console.log(docHeight);    // 1647

Does anyone know why this happens?

Edit 2:

The above behavior is also non deterministic, but the decimal part is true.

testing
  • 2,103
  • 2
  • 14
  • 28
  • 1
    this is a rather unusual way to design an angular directive; it's counter to the design principles of angular to start tying the execution of JavaScript to specific UI events. – Claies Jul 28 '16 at 18:02
  • You do not happen to have the chrome dev console at the bottom of the screen? Seems to me that it can have a bit quirky affect on the height perception of similar code. – cYrixmorten Jul 28 '16 at 18:02
  • @cYrixmorten I didn't have chrome dev console at the bottom of the screen, although I did tried that, but the behavior is still non deterministic. – testing Aug 02 '16 at 18:42
  • 1
    While this isn't necessarily a fix, it may still be a fix... since it's off by 0.1 to 0.9, couldn't you use floor or ceiling? – Chris Stanley Aug 11 '16 at 17:19
  • @ChrisStanley that is exactly what I did, but I want to know the reason though. – testing Aug 12 '16 at 17:38
  • You can just do `Math.floor` or `Math.Ceil` on the value so you will get proper values without decimals. – Stan Aug 15 '16 at 19:10
  • Can't you use an "infinite loading" module for angular (or vanilla js) and change it to load your directive ? It handles the bottom of page detection rather nicely. – Andrew Donovan Aug 16 '16 at 13:00

2 Answers2

3

0.1 + 0.2 !== 0.3

This one is an oddity not just in JavaScript; it’s actually a prevailing problem in computer science, and it affects many languages. The output of this is 0.30000000000000004.

This has to do with an issue called machine precision. When JavaScript tries to execute the line above, it converts the values to their binary equivalents. This is where the problem starts. 0.1 is not really 0.1 but rather its binary equivalent, which is a near-ish (but not identical) value. In essence, as soon as you write the values, they are doomed to lose their precision. You might have just wanted two simple decimals, but what you get, as Chris Pine notes, is binary floating-point arithmetic. Sort of like wanting your text translated into Russian but getting Belorussian. Similar, but not the same.

You can read more here. Without digging into browser source, I would guess that your problem stems from this.

Community
  • 1
  • 1
adamdport
  • 8,779
  • 11
  • 58
  • 83
  • Downvoted. Does not answer the OP's question or solves the issue. – DhruvPathak Aug 16 '16 at 07:08
  • Up vote. It does answer the question. If you read the addition to the question it turns out the issue is from how JavaScript handles mathematical operations. – Michael Warner Aug 16 '16 at 13:19
  • @DhruvPathak "Does anyone know why this happens?" is ultimately OP's question as he's already discovered the culprit on his own – adamdport Aug 16 '16 at 13:58
3

Given the floating-point precision issues, you may want to loosen you condition to check instead if the two values are less than 1 pixel different. For example:

if (Math.abs(windowBottom - docHeight) < 1) {
    scope.$apply(attribute.myDirective);
}
mcgraphix
  • 2,568
  • 1
  • 9
  • 15