Stick Div Inside Parent Div with JavaScript

Advertisement

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

Follow the below steps:

Create a HTML File Create a HTML File

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

<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;
            }
        </style>
    </head>
    <body>
        <div class="parent">
            <div class="child"></div>
        </div>
    </body>
</html>

You can see the output in browse as below:

Stick Div Inside Parent Div with JavaScript 1
Stick Div Inside Parent Div with JavaScript 3

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>
Stick Div Inside Parent Div with JavaScript 2
Stick Div Inside Parent Div with JavaScript 4

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 the above code and add the below one in our index.html file.

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

If you refresh our page on the browser and scroll on the 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 crosses 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 the above video, you can see the child’s div cross the parent’s height. In the next section, we are going to see how to keep sticking our child div inside our parent div.

To stick the child div inside the parent we have checked 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 crosses the 0 then we add the fixed class and remove it if the parent top is above the 0.

To make it work you need to add the 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 Div Keep Child Div Inside Parent Div

With the 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 reaches the bottom of our parent div.

In the above video, you can see that if our parent top offset reaches the height of our child div then it adds 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 the parent’s bottom offset crosses our child’s height then we add the absolute class.

To make it work add the 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 added and removed the fixed and absolute CSS classes to keep our child div scrolling inside our parent div.

  • We add the absolute CSS class and remove the fixed css class if our parent crosses the height of our child div.
  • Also, We add the fixed CSS class and remove the absolute css class if our child div crosses 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>

Top ↑

Live Preview Live Preview

See the Pen Smooth stick div inside parent div with Plain JavaScript by Mahesh Waghmare (@maheshwaghmare) on CodePen.

Leave a Reply