DEV Community

Cover image for Follow Mouse on Hover in SCSS with Responsive. No JS!
Prahalad S
Prahalad S

Posted on

Follow Mouse on Hover in SCSS with Responsive. No JS!

Follow Mouse on Hover in SCSS with Responsive. No JS!

So Let's create hover follow mouse using scss without using JavaScript. Below is the gif image for final output. Its also responsive.

Image description

Let's create list elements in html like below code. Also add <span class="item"></span> at the bottom of last <li>. This item span will follow the mouseover.

<ul id="list">
        <li class="box"><img src="img/1.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/2.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/3.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/4.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/5.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/6.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/7.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/8.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/9.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/10.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/11.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/12.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/13.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/14.jpg" alt=""><span>caption</span></li>
        <li class="box"><img src="img/15.jpg" alt=""><span>caption</span></li>
     <span class="item"></span>
</ul>
Enter fullscreen mode Exit fullscreen mode

Let's create scss variables $w, $h for width and height of <li>. Assign width, height to 100% to img and span(which has text: caption) tags which are inside <li> like below code. span will appear on hover of <li> and also <span class="item"></span> will follow on hover with our for loop logic.

$w: 150px;
$h: 100px;

body {
    margin: 0;
    padding: 0;
    background: #5e5e5e;
    font-family: monospace;
}

#list {
    display: flex;
    flex-wrap: wrap;
    margin: 50px auto;
    width:750px;
    padding: 0;
    list-style-type: none;
    position: relative;

    li {
        &.box {
            box-sizing: border-box;
            -moz-box-sizing: border-box;
            -webkit-box-sizing: border-box;
            width: $w;
            height: $h;
            position: relative;
            overflow: hidden;
            border: 1px solid rgb(255, 255, 255);
            z-index: 1;
            margin: 0;
            padding: 0;
            cursor: pointer;
            span {
                font-size: 15px;
                opacity: 0;
                z-index: 2;
                position: absolute;
                color: white;
                width: 100%;
                height: 100%;
                left: 0;
                top: 0;
                z-index: 2;
                text-shadow: none;
                display: flex;
                align-items: center;
                justify-content: center;
            }
        }

        img {
            width: 100%;
            height: 100%;
            opacity: 0.5;
        }

        &:hover {
            span {
                opacity: 1;
                animation: fade 0.5s;
            }

            img {
                opacity: 0.15;
            }
        }
    }

    .item {
        position: absolute;
        width: $w;
        height: $h;
        left: unset;
        top: unset;
        background: rgb(0, 0, 0);
        opacity: 0;
        z-index: 0;
        transition: 0s ease;
        pointer-events: none;
    }
}

@keyframes fade {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

Enter fullscreen mode Exit fullscreen mode

Output:

Image description

Now, I have three methods to do this. First lets go with first method. Please comment which method is better. Feedback is much appreciated.

Method 1:
First we have to calculate columns & rows. Like how we want our images to display. I want to display 5x3(5 columns, 3 rows). 5 images in a row. Our total images are 15. So, we get 5 images in each row. Let's declare for columns as $col: 5; rows as $numOfRows: 3; Now, on hover on any <li>. we will move the <span class="item"></span> on it. Now lets write for loop. Lets also add for loop in media queries too.

#list {
    $col: 5; // when we have 5 images in a row
    $numOfRows: 3; // when we have 3 rows 
    width: 750px; //  each <li> width is 150, 150x5=750;
    $max-nth-child : 15; // total <li>'s are 15.
    $limit: min($col * $numOfRows, $max-nth-child);

    @for $i from 1 through $limit {
        li {
            &.box:nth-child(#{$i}):hover ~.item {
                $row: ceil($i / $col); // determine the row number
                $rowOffset: ($row - 1) * $col * $w; // adjust based on row
                left: #{$i * $w - $rowOffset - ($w)};
                top: #{($row - 1) * $h};
                transition: 0.25s ease;
                opacity: 0.7;
            }
        }
    }
}

@media only screen and (max-width: 768px) { 
    #list {
        $col: 4; // when we have 5 images in a row
        $numOfRows: 4; // when we have 3 rows 
        width: 600px; //  each <li> width is 150, 150x4=600;
        $max-nth-child : 15; // total <li>'s are 15.
        $limit: min($col * $numOfRows, $max-nth-child);

        @for $i from 1 through $limit {
            li {
                &.box:nth-child(#{$i}):hover ~.item {
                    $row: ceil($i / $col); // determine the row number
                    $rowOffset: ($row - 1) * $col * $w; // adjust based on row
                    left: #{$i * $w - $rowOffset - ($w)};
                    top: #{($row - 1) * $h};
                    transition: 0.25s ease;
                    opacity: 0.7;
                }
            }
        }
    }
}

@media only screen and (max-width: 600px) { 
    #list {
        $col: 3; // when we have 5 images in a row
        $numOfRows: 5; // when we have 3 rows 
        width: 450px; //  each <li> width is 150, 150x3=600;
        $max-nth-child : 15; // total <li>'s are 15.
        $limit: min($col * $numOfRows, $max-nth-child);

        @for $i from 1 through $limit {
            li {
                &.box:nth-child(#{$i}):hover ~.item {
                    $row: ceil($i / $col); // determine the row number
                    $rowOffset: ($row - 1) * $col * $w; // adjust based on row
                    left: #{$i * $w - $rowOffset - ($w)};
                    top: #{($row - 1) * $h};
                    transition: 0.25s ease;
                    opacity: 0.7;
                }
            }
        }
    }
}

@media only screen and (max-width: 450px) { 
    #list {
        $col: 2; // when we have 5 images in a row
        $numOfRows: 8; // when we have 3 rows 
        width: 300px; //  each <li> width is 150, 150x2=300;
        $max-nth-child : 15; // total <li>'s are 15.
        $limit: min($col * $numOfRows, $max-nth-child);

        @for $i from 1 through $limit {
            li {
                &.box:nth-child(#{$i}):hover ~.item {
                    $row: ceil($i / $col); // determine the row number
                    $rowOffset: ($row - 1) * $col * $w; // adjust based on row
                    left: #{$i * $w - $rowOffset - ($w)};
                    top: #{($row - 1) * $h};
                    transition: 0.25s ease;
                    opacity: 0.7;
                }
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

That's it! We are done with responsive too.
Output default: max-width >= 1920px (5x3, col and rows) :

Image description

Output max-width: 768px (4x4, col and rows) :
Image description

Output max-width: 600px (3x5, col and rows) :
Image description

Output max-width: 450px (2x8, col and rows) :
Image description

Method 2:

@mixin responsive-styles($width, $col, $multiplier, $max-nth-child) {
    #list {
        width: $width;

        // Calculate the maximum range for the loop
        $limit: min($col * $multiplier, $max-nth-child);

        @for $i from 1 through $limit {
            li {
                &.box:nth-child(#{$i}):hover~.item {
                    $row: ceil($i / $col); // determine the row number
                    $rowOffset: ($row - 1) * $col * $w; // adjust based on row
                    left: #{$i * $w - $rowOffset - ($w)};
                    top: #{($row - 1) * $h};
                    transition: 0.25s ease;
                    opacity: 0.7;
                }
            }
        }
    } 
}
/* Base styles */

/* uncomment below #list if you don't want to use media query for 
(max-width: 1920px) and comment the below media query which has 
[(max-width: 1920px), @include responsive-styles(750px, 5, 3);] */

// #list { 
//     $col: 5;
//     width: 750px;
//     $multiplier : 3;
//     $max-nth-child: 15;
//     // Calculate the maximum range for the loop
//     $limit: min($col * $multiplier, $max-nth-child);
//     @for $i from 1 through $limit {
//         li {
//             &.box:nth-child(#{$i}):hover ~.item { 
//                 $row: ceil($i / $col); // determine the row number
//                 $rowOffset: ($row - 1) * $col * $w; // adjust based on row
//                 left: #{$i * $w - $rowOffset - ($w)};
//                 top: #{($row - 1) * $h};
//                 transition: 0.25s ease;
//                 opacity: 0.7;
//             }
//         }
//     }
// }

// media queries
@media only screen and (max-width: 1920px) { 
   @include responsive-styles(750px, 5, 3, 15);
}

@media only screen and (max-width: 768px) { 
    @include responsive-styles(600px, 4, 4, 15);
 }

 @media only screen and (max-width: 600px) { 
    @include responsive-styles(450px, 3, 5, 15);
 }

 @media only screen and (max-width: 450px) { 
    @include responsive-styles(300px, 2, 8, 15);
 }
Enter fullscreen mode Exit fullscreen mode

Output:

Image description
Method 3:

$max-nth-child : 15; // total <li>'s are 15.
$breakpoints: ( 
        1920px:(col: 5, numOfRows: 3, width: 750px),
        768px: (col: 4, numOfRows: 4, width: 600px),
        600px: (col: 3, numOfRows: 5, width: 450px),
        450px: (col: 2, numOfRows: 8, width: 300px));

/* uncomment this below commented seection from 'width:750px to end of loop' if you want to
comment above first breakpoint(1920) */
#list {
    // $default-col: 5;
    // $default-numOfRows: 3;
    // width: 750px;
    // $col: $default-col;
    // $numOfRows: $default-numOfRows;
    // $limit: min($col * $numOfRows, $max-nth-child);

    // @for $i from 1 through $col * $numOfRows {
    //     li {
    //         &.box:nth-child(#{$i}):hover ~.item { 
    //             $row: ceil($i / $col); // determine the row number
    //             $rowOffset: ($row - 1) * $col * $w; // adjust based on row
    //             left: #{$i * $w - $rowOffset - ($w)};
    //             top: #{($row - 1) * $h};
    //             transition: 0.25s ease;
    //             opacity: 0.7;
    //         }
    //     }
    // }

    @each $breakpoint, 
    $settings in $breakpoints {
        @media only screen and (max-width: $breakpoint) {
            width: map-get($settings, width);
            $col: map-get($settings, col);
            $numOfRows: map-get($settings, numOfRows);

            $limit: min($col * $numOfRows, $max-nth-child);

            @for $i from 1 through $limit {
                li {
                    &.box:nth-child(#{$i}):hover~.item {
                        $row: ceil($i / $col); // determine the row number
                        $rowOffset: ($row - 1) * $col * $w; // adjust based on row
                        left: #{$i * $w - $rowOffset - ($w)};
                        top: #{($row - 1) * $h};
                        transition: 0.25s ease;
                        opacity: 0.7;
                    }
                }
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Output:
Image description

Thank you for watching...Please subscribe to my youtube channel

Click here for demo

Top comments (0)