Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

javascript getBoundingClientRect() values are wrong for dynamically added elements

Tags:

javascript

Not able to find an answer on wide web so posting the question here.

What I am trying to achieve:

When the user scrolls to the very bottom of the page get the latest dynamically loaded element's bottom value and use it to find out if it is time to load another one.

The math is simple:

if (element.getBoundingClientRect().bottom <= window.innerHeight)
   loadAnotherElement();

window.innerHeight is 955px

The problem:

On initial load the the first element's bottom value is 905px which is fine and trigger the function to load another one, but after the second one is loaded in the bottom value is 1389px which will never trigger the loadAnotherElement function.

I am not able to post full code as it is too complicated so hope the above will be enough to understand.

EDIT

Managed to create a proper test case

like image 234
Morpheus Avatar asked Apr 15 '16 15:04

Morpheus


People also ask

Why is getBoundingClientRect not working?

The "getBoundingClientRect is not a function" error occurs for multiple reasons: calling the getBoundingClientRect() method on a value that is not a DOM element. placing the JS script tag above the code that declares the DOM elements. misspelling getBoundingClientRect (it's case sensitive).

What is getBoundingClientRect()?

getBoundingClientRect() method returns a DOMRect object providing information about the size of an element and its position relative to the viewport.

Does getBoundingClientRect include margin?

margin is not included.


1 Answers

In the JS fiddle you posted the reason it was not recognising the correct height is because your inner elements are floating. A float does not contribute to the height of the parent element, so I will suggest the following solution:

article:after {
    width: 100%;
    clear: both;
    content: '';
    display: block;
}

I have also cleaned up the fiddle a bit an removed the unnecessary parts so it's easier to see where the mistake was (there was no advantage to the table style displayed before and after on your section).

var last = document.querySelector('article');
document.addEventListener('scroll', function(){
    if(last.getBoundingClientRect().bottom <= window.innerHeight){
        var newElement = last.cloneNode(true);
        last.parentNode.appendChild(newElement);
        last = newElement;
    }
});
html,
body {
    height: 100%;
}

/* I have removed a bit of CSS that I think didn't really do anything at all. */

article {
    width: 100%;
    display: inline-block;
    border-bottom: 1px solid red;
}

/* This will create an element that will clear past the floats, stretching your article to the correct encapsulating size */

article:after {
    width: 100%;
    clear: both;
    content: '';
    display: block;
}

/* Some correction so they all stay neatly in line */

div {
    float: left;
    width: 30%;
    margin: 1.5%;
    height: 100vh;
    position: relative;
    background: blue;
}

div:nth-child(3n-2){ background: #666; }
div:nth-child(3n-1){ background: #888;  }
div:nth-child(3n){ background: #222; }
<section>
  <article>
    <!-- I made these 6 divs so they neatly pack six in an article. -->
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
  </article>
</section>

Update

If you want the elements to neatly stay in line, why bother wrapping them in seperate elements? Simple create the elements as new siblings which will push its final size:

var section = document.querySelector('section');
var article = document.querySelector('article');
document.addEventListener('scroll', function(){
    if(section.getBoundingClientRect().bottom <= window.innerHeight){
        section.appendChild(article.cloneNode(true));
    }
});
html,
body {
    height: 100%;
}

/* I have removed a bit of CSS that I think didn't really do anything at all. */

section {
    width: 100%;
    display: inline-block;
    border-bottom: 1px solid red;
}

/* This will create an element that will clear past the floats, stretching your article to the correct encapsulating size */

section:after {
    width: 100%;
    clear: both;
    content: '';
    display: block;
}

/* Some correction so they all stay neatly in line */

div {
    float: left;
    width: 30%;
    margin: 1.5%;
    height: 100vh;
    position: relative;
    background: blue;
}

div:nth-child(3n-2){ background: #666; }
div:nth-child(3n-1){ background: #888;  }
div:nth-child(3n){ background: #222; }
<section>
  <article>
    <!-- I made these 7 divs for illustrations sake. -->
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
  </article>
</section>
like image 176
somethinghere Avatar answered Sep 17 '22 08:09

somethinghere