Full Page Slider

Full Page Slider

New week! New Challenge! 😄

The theme for the second week of the Weekly Coding Challenge is:

Create a Slider

A slider is used mostly to showcase content like: images, projects in your portfolio or even testimonials from clients. It is useful because you have this content focused in a screen which changes at a certain time, like every 5 seconds, and highlights the next item from your showcase.

In this article we're going to build the following Full Page Slider:

https://codepen.io/FlorinPop17/pen/LvOroe/

As you can see it has a fun little animation which erases the current item and highlights the next one. The next slide comes in automatically (after a few seconds) but you could also change the slide by clicking on the navigation buttons, which is pretty neat if you ask me! 😃

Let's see how we can build this!

Gathering the resources

First thing that we need to do is to get the images which will be used in the slider. For this, Unsplash is the perfect place! It has beautiful, free photos which could be used in any project!

Next, we need the icons for the navigation buttons. As always I choose to use FontAwesome.

The HTML

We'll add everything in a main .slider div, and by everthing I mean the following:

  1. a .slider-container div which will hold all the .slide's with the images (the class will be used in JavaScript to get all the .slide children of this div)
  2. an .eraser div which will be used in the erasing animation. Basically this will slowly hide the current item in the slider, and when it reaches the end, we quickly swap it out with the next .slide
  3. the .buttons-container which holds the two navigation buttons and it has two FontAwesome icons
<div class="slider">
    <div class="slider-container">
        <!-- See bellow for the content of this div -->
    </div>
    <div class="eraser"></div>
    <div class="buttons-container">
        <button id="previous"><i class="fa fa-arrow-left"></i></button>
        <button id="next"><i class="fa fa-arrow-right"></i></button>
    </div>
</div>

Now, for the .slides...

We put the image directly in the style prop of the <div>. This is because I don't want to have in the CSS multiple declarations for each .slide separately just to add a background-image property. It's also useful if in the future we want to convert this slider and have the .slides added dynamically using React or Vue or anything else. 🙂

Also, we add a div (.info) within it that will have some additional text we want to display on top of the image. This is optional, you can remove it if you want.

<div class="slider">
    <div class="slider-container">
        <div
            class="slide active"
            style="background-image: url('add_image_url_here');"
        >
            <div class="info">
                <h1>Ocean View</h1>
                <p>
                    Lorem ipsum dolor sit amet consectetur adipisicing elit.
                    Voluptatum.
                </p>
            </div>
        </div>
        <div class="slide" style="background-image: url('add_image_url_here');">
            <div class="info">
                <h1>Forest View</h1>
                <p>
                    Lorem, ipsum dolor sit amet consectetur adipisicing elit.
                    Amet, vero earum perspiciatis officia
                </p>
            </div>
        </div>
        <div class="slide" style="background-image: url('add_image_url_here');">
            <div class="info">
                <h1>Ocean View</h1>
                <p>
                    Lorem ipsum dolor sit amet consectetur adipisicing elit.
                    Incidunt recusandae unde autem
                </p>
            </div>
        </div>
        <!-- The rest of the slides -->
    </div>
    <!-- The rest of the content -->
</div>

Important

The first .slide has an .active class set on it. We'll use this later to track in the JavaScript which .slide is visible/active at a certain time.

Note that the script will fail if

  • you have multiple .slides with an .active class or
  • you don't have a .slide with an .active class

The CSS

As we want the Slider to be Full Width, we need to either use Normalize CSS or to have some simple resets in the CSS.

@import url('https://fonts.googleapis.com/css?family=Lato');

* {
    box-sizing: border-box;
}

body {
    font-family: 'Lato', sans-serif;
    margin: 0;
}

(I also added the Lato font as I decided to have some text in the Slider. You can remove the import if you don't have any text or you can add your own font to the body tag).

The .slides will need to be positioned absolute if we want to move them around and that means that we'll have to have a relative positioned parent, that would be the .slider div.

.slider {
    position: relative;
    overflow: hidden;
    height: 100vh;
    width: 100vw;
}

.slide {
    background-position: center center;
    background-size: cover;
    position: absolute;
    top: 0;
    left: 100%;
    height: 100%;
    width: 100%;
}

.slide.active {
    transform: translateX(-100%);
}

Few things to note here:

  • 100vh and 100vw means that the .slider should be as wide as the screen (vh - viewport height, vw - viewport width)
  • initially we have the .slide positioned outside the viewport with the left property set to 100%, and we'll translate it back to 0 when the .active class is set on it (left: 100% + translateX(-100%) = left: 0)

The next piece of code is optional. Only use it if you want to have the text over the .slide:

.slide .info {
    background-color: rgba(255, 255, 255, 0.7);
    color: #333;
    padding: 20px 15px;
    position: absolute;
    opacity: 0;
    top: 70px;
    left: 30px;
    text-align: center;
    width: 300px;
    max-width: 100%;
}

.slide.active .info {
    opacity: 1;
    transform: translateY(-40px);
    transition: all 0.5s ease-in-out 0.6s;
}

.slide .info h1 {
    margin: 10px 0;
}

.slide .info p {
    letter-spacing: 1px;
}

There's a transition which will add the moving up effect to the .info div and also will change the opacity, just a little touch I wanted to add. 🙂 Also there is a delay to the transition of 0.6s - this is enough to wait until the .eraser is moving out of the way so the text transition is visible.

Speaking of which... The .eraser CSS:

.eraser {
    background: #fff;
    position: absolute;
    transition: transform 0.7s ease-in-out;
    top: 0;
    left: 100%;
    height: 100%;
    width: 100%;
    z-index: 100;
}

.eraser.active {
    transform: translateX(-100%);
}

The .eraser is another full height/width div which has it's own .active class (do not confuse this with the .slide's active class). This class with be added in JavaScript when we want to change the slide. More about this a little bit later. 😉

Note: the 0.7s value is important as this will have to be exactly the same with the eraserActiveTime value used in the JavaScript.

And the last part of the CSS, the .buttons-container:

.buttons-container {
    position: absolute;
    bottom: 60px;
    right: 20px;
}

.buttons-container button {
    border: 2px solid #fff;
    background-color: transparent;
    color: #fff;
    cursor: pointer;
    padding: 8px 20px;
}

.buttons-container button:hover {
    background-color: #fff;
    color: #333;
}

Simple CSS, nothing fancy here. We just position this div absolute too so we can place it wherever we want in the .slider.

The JavaScript

This part is where the magic happens. First, we need to set all the variables that we're going to use (I added comments for all the lines, it should be pretty clear):

const slides = document.querySelectorAll('.slider-container .slide'); // get all the slides
const eraser = document.querySelector('.eraser'); // the eraser
const prev = document.getElementById('previous'); // previous button
const next = document.getElementById('next'); // next button
const intervalTime = 5000; // time until nextSlide triggers in miliseconds
const eraserActiveTime = 700; // time to wait until the .eraser goes all the way
let sliderInterval; // variable used to save the setInterval and clear it when needed

Next, we need to create a function .nextSlide which will be called every 5 seconds - the intervalTime. Let's break this function into steps:

  1. Add the .active class to the eraser - this will trigger the eraser to move to the left.
  2. Set a timeout that will allow the eraser to move all the way to the left. This is where we'll use the eraserActiveTime - it has to be the same as the CSS value we mentioned above.
  3. Get the active .slide and toggle the .active class on it (in this case, remove it).
  4. Check to see if the .slide has a next element sibling available. If it has, add the .active class to it.
  5. If it's the last element in the list, add the .active class to the first slide (the one with index 0).
  6. Remove the .active class from the eraser - this will trigger the eraser to move back to the right. It also waits 200 ms before doing this (just to give enough time for the next .slide to move in place).
const nextSlide = () => {
    // Step 1.
    eraser.classList.add('active');

    // Step 2.
    setTimeout(() => {
        // Step 3.
        const active = document.querySelector('.slide.active');
        active.classList.toggle('active');

        // Step 4.
        if (active.nextElementSibling) {
            active.nextElementSibling.classList.toggle('active');
        } else {
            // Step 5.
            slides[0].classList.toggle('active');
        }

        // Step 6.
        setTimeout(() => {
            eraser.classList.remove('active');
        }, 200);
    }, eraserActiveTime);
};

sliderInterval = setInterval(nextSlide, intervalTime);

At the end, we set an interval which will run every intervalTime miliseconds and it will change the slide to the next one, over and over.

The buttons functionality

As of now we have a slider which will automatically change slides every intervalTime miliseconds, but we also want to be able to change the .slide's manually by pressing the buttons. Before that though, let's quickly add the prevSlide function. This is very similar to the nextSlide function, but instead of going to the next sibling, we go to the previous sibling and when we reset, we set the last slide to be active:

const prevSlide = () => {
    eraser.classList.add('active');
    setTimeout(() => {
        const active = document.querySelector('.slide.active');
        active.classList.toggle('active');

        // The *changed* part from the nextSlide code
        if (active.previousElementSibling) {
            active.previousElementSibling.classList.toggle('active');
        } else {
            slides[slides.length - 1].classList.toggle('active');
        }
        // End of the changed part

        setTimeout(() => {
            eraser.classList.remove('active');
        }, 200);
    }, eraserActiveTime);
};

Good, we have the functions in place. All we have to do now is to add the eventListeners to the buttons:

next.addEventListener('click', () => {
    nextSlide();
    clearInterval(sliderInterval);
    sliderInterval = setInterval(nextSlide, intervalTime);
});

prev.addEventListener('click', () => {
    prevSlide();
    clearInterval(sliderInterval);
    sliderInterval = setInterval(nextSlide, intervalTime);
});

This is where we also need to reset the interval as we don't want it to run while we are pressing the buttons. For this we clear the "old" interval and create a new one.

Aaaand..... we're done! 😃

Conclusion

Congratulations for making it this far! 👏 I hope you liked this slider example and I can't wait to see what you're going to build next! 😄

Remember to check out the Weekly Coding Challenge "rules" if you want to participate! It's a great way to learn by practicing!

Have fun! I surely did! 👍

Tagged with html5, css3, animation, keyframes, transition, transform, slider, javascript, weekly-coding-challenge