Stick div inside parent div

Advertisement

You are going to learn:

See below short video of what we are trying to achive with plain JavaScript:

Create Example File Create Example File

Create a new file index.html and add below code within it.

<pre class="wp-block-syntaxhighlighter-code">&lt;html>
    &lt;head>
        &lt;title>Stick div inside parent div&lt;/title>
        &lt;style>
            body {
                margin: 0;
                padding: 0;
                background: #ffffff;
                height: 300vh;
            }
            .parent {
                background: #03A9F4;
                height: 100vh;
                margin: 20em 0;
                position: relative;
            }
            .child {
                height: 200px;
                width: 200px;
                margin: 0 auto;
                left: 0;
                background: #673AB7;
                right: 0;
            }
        &lt;/style>
    &lt;/head>
    &lt;body>
        &lt;div class="parent">
            &lt;div class="child">&lt;/div>
        &lt;/div>
    &lt;/body>
&lt;/html></pre>

You can see the outout in browse as below:

Top ↑

Understand clientHeight and getBoundingClientRect Understand clientHeight and getBoundingClientRect

To achieve the solution we need to understand about clientHeight and getBoundingClientRect().

Open the index.html file add below code before closing the <body> tag.

<script>
            let parent = document.querySelector('.parent');
            console.log( parent.clientHeight );
            console.log( parent.getBoundingClientRect() );
</script>

Here,

  • parent.clientHeight – Return the 334 which is the height of our parent div.
  • parent.getBoundingClientRect() – It returns the DOMRect object which show height, width and offset of our parent div.
DOMRect {
    bottom: 654
    height: 334
    left: 0
    right: 1349
    top: 320
    width: 1349
    x: 0
    y: 320
}

We are going to implement our logic with the help of our parent and child div height, offset and positions.

Top ↑

Add scroll event Add scroll event

now, remove above code and add below one in our index.html file.

<script>
function handleScroll() {
     console.log( 'here' );
}
document.addEventListener( 'scroll', handleScroll );
</script>

If you refresh our page on browser and scroll on page then you can see the console message here.

Add Selectors Add Selectors

Inside the handleScroll() function add below code:

let parent = document.querySelector('.parent');
let child = parent.querySelector('.child');

Here we have defined our parent and child selectors.

Top ↑

Stick if cross parent top Stick if cross parent top

Now, We are going to add the fixed class to our parent div if our child div cross the parent div top offset.

See video:

In the video you can see the fixed class is added if we cross the top of our parent div.

NOTE: In above video you can see the child div cross the parent height. In next section we are going to see how to keep stick our child div inside our parent div.

To stick the child div inside the parent we have check the

let parentTop = parent.getBoundingClientRect().top || 0;
if( parentTop <= 0 ) {
    parent.classList.add( 'fixed' );
} else {
    parent.classList.remove( 'fixed' );
}

On scroll if our parent top cross the 0 then we add the fixed class and remove it if parent top above the 0.

To make it work you need to add below CSS into our index.html file too.

.fixed .child {
    position: fixed;
    top: 0;
    bottom: auto;
}

Above CSS simply fix our child div.

Top ↑

Keep child div inside parent Keep child div inside parent

With above code we can able to stick our child div inside the parent if we cross the parent div.

Now, We are building the logic to unstick our child div if it reach to the bottom of our parent div.

In above vide you can see that if our parent top offset reach the height of our child div then it add the absolute class.

function handleScroll() {
    let parent = document.querySelector('.parent');
    let child = parent.querySelector('.child');
    let crossChildHeight = 0;
    if( child.clientHeight > parent.getBoundingClientRect().bottom ) {
        crossChildHeight = parent.getBoundingClientRect().bottom - child.clientHeight;
    }
    if( parseInt( crossChildHeight ) < 0 ) {
        parent.classList.add( 'absolute' );
    } else {
        parent.classList.remove( 'absolute' );
    }
}

Here, We checked if parent bottom offset cross our child height height then we add the absolute class.

To make it work add below CSS:

.absolute .child {
    position: absolute;
    bottom: 0;
    top: auto;
}

Top ↑

Combine Parent top & bottom Cross Combine Parent top & bottom Cross

let parent = document.querySelector('.parent');
let child = parent.querySelector('.child');
let crossChildHeight = 0;
if( child.clientHeight > parent.getBoundingClientRect().bottom ) {
    crossChildHeight = parent.getBoundingClientRect().bottom - child.clientHeight;
}
if( parseInt( crossChildHeight ) < 0 ) {
    parent.classList.remove( 'fixed' );
    parent.classList.add( 'absolute' );
} else {
    let parentTop = parent.getBoundingClientRect().top || 0;
    parent.classList.remove( 'absolute' );
    if( parentTop <= crossChildHeight ) {
        parent.classList.add( 'fixed' );
    } else {
        parent.classList.remove( 'fixed' );
    }
}

Here, We have simply add and remove the fixed and absolute CSS classes to keep our child div scroll inside our parent div.

  • We add the absolute css class and remove fixed css class if our parent cross the height of our child div.
  • Also, We add the fixed css class and remove absolute css class if our child div cross the top offset of our parent div.

Top ↑

Complete Code Complete Code

<html>
    <head>
        <title>Stick div inside parent div</title>
        <style>
            body {
                margin: 0;
                padding: 0;
                background: #ffffff;
                height: 300vh;
            }
            .parent {
                background: #03A9F4;
                height: 100vh;
                margin: 20em 0;
                position: relative;
            }
            .child {
                height: 200px;
                width: 200px;
                margin: 0 auto;
                left: 0;
                background: #673AB7;
                right: 0;
            }
            .absolute .child {
                position: absolute;
                bottom: 0;
                top: auto;
            }
            .fixed .child {
                position: fixed;
                top: 0;
                bottom: auto;
            }
        </style>
    </head>
    <body>
        <div class="parent">
            <div class="child"></div>
        </div>
        <script>
            function handleScroll() {
                // Define selectors.
                let parent = document.querySelector('.parent');
                let child = parent.querySelector('.child');
                let parentTop = parent.getBoundingClientRect().top || 0;
                let crossChildHeight = 0;
                if( child.clientHeight > parent.getBoundingClientRect().bottom ) {
                    crossChildHeight = parent.getBoundingClientRect().bottom - child.clientHeight;
                }
                if( parseInt( crossChildHeight ) < 0 ) {
                    parent.classList.remove( 'fixed' );
                    parent.classList.add( 'absolute' );
                } else {
                    parent.classList.remove( 'absolute' );
                    if( parentTop <= crossChildHeight ) {
                        parent.classList.add( 'fixed' );
                    } else {
                        parent.classList.remove( 'fixed' );
                    }
                }
            }
            document.addEventListener( 'scroll', handleScroll );
                
        </script>
    </body>
</html>
%d bloggers like this: