How I Created My Homepage JavaScript Post Carousel

👋 Ciprian on Tuesday, April 27, 2021 in Blog, JavaScript
Last modified on Sunday, April 25, 2021

Learn JavaScript by example. Code snippets, how-to's and tutorials. Try now!

My homepage now features a JavaScript section of 4 featured posts. Notice the official JavaScript yellow colour 😊 Here’s how I did it.

Note that this is mostly a WordPress feature, so it uses a loop to get all the content (titles, links, excerpts and images), but it can be easily adapted to any CMS or static site. Just remove the loop and replace the JavaScript const arrays.

Demo

Click the up and down arrows to see a selection of 4 featured JavaScript articles:

HTML/PHP/JS Code

Here’s my current HTML/JS structure. I opted for inline JS, as it can be compressed and served immediately, without an additional HTTP request. For readability, I’ll leave the JavaScript unminified.

<?php
$args = [
    'post_status' => 'publish',
    'post_type' => 'post',
    'posts_per_page' => 4,
    'category_name' => 'javascript'
];

$data = '<div id="carousel-wrapper">
    <div id="menu">
        <div id="current-option">
            <a id="current-option-permalink" href=""><span id="current-option-text1" data-previous-text="" data-next-text=""></span></a>
            <span id="current-option-text2" data-previous-text="" data-next-text=""></span>
        </div>
        <div id="image"></div>
    </div>

    <button id="previous-option"></button>
    <button id="next-option"></button>
</div>';

$featuredQuery = new WP_Query($args);

$titles = [];
$excerpts = [];
$permalinks = [];
$illustrations = [];

if ($featuredQuery->have_posts()) {
    while ($featuredQuery->have_posts()) {
        $featuredQuery->the_post();

        $postID = get_the_ID();

        $excerpt = substr(get_the_excerpt(), 0, 140);

        $titles[] = get_the_title($postID);
        $excerpts[] = substr($excerpt, 0, strrpos($excerpt, ' '));
        $permalinks[] = get_permalink($postID);
        $illustrations[] = get_the_post_thumbnail_url($postID, 'medium');
    }
}

$titles = implode(',', array_map('whiskey_add_quotes', $titles));
$excerpts = implode(',', array_map('whiskey_add_quotes', $excerpts));
$permalinks = implode(',', array_map('whiskey_add_quotes', $permalinks));
$illustrations = implode(',', array_map('whiskey_add_quotes', $illustrations));

$data .= '<script>
const text1_options = [' . $titles . '];
const text2_options = [' . $excerpts . '];
const color_options = ["#f7df1e", "#f7df1e", "#f7df1e", "#f7df1e"];
const image_options = [' . $illustrations . '];
const permalink_options = [' . $permalinks . '];

var i = 0;
const currentOptionText1 = document.getElementById("current-option-text1");
const currentOptionText2 = document.getElementById("current-option-text2");
const currentOptionImage = document.getElementById("image");
const currentOptionPermalink = document.getElementById("current-option-permalink");

const carousel = document.getElementById("carousel-wrapper");
const mainMenu = document.getElementById("menu");
const optionPrevious = document.getElementById("previous-option");
const optionNext = document.getElementById("next-option");

currentOptionText1.innerText = text1_options[i];
currentOptionText2.innerText = text2_options[i];
currentOptionImage.style.backgroundImage = "url(" + image_options[i] + ")";
currentOptionPermalink.href = permalink_options[i];
mainMenu.style.background = color_options[i];

optionNext.onclick = function () {
    i = i + 1;
    i = i % text1_options.length;
    currentOptionText1.dataset.nextText = text1_options[i];
    currentOptionText2.dataset.nextText = text2_options[i];

    mainMenu.style.background = color_options[i];
    carousel.classList.add("anim-next");
  
    setTimeout(() => {
        currentOptionImage.style.backgroundImage = "url(" + image_options[i] + ")";
    }, 455);
  
    setTimeout(() => {
        currentOptionText1.innerText = text1_options[i];
        currentOptionText2.innerText = text2_options[i];
        currentOptionPermalink.href = permalink_options[i];
        carousel.classList.remove("anim-next");
    }, 650);
};

optionPrevious.onclick = function () {
    if (i === 0) {
        i = text1_options.length;
    }
    i = i - 1;
    currentOptionText1.dataset.previousText = text1_options[i];
    currentOptionText2.dataset.previousText = text2_options[i];

    mainMenu.style.background = color_options[i];
    carousel.classList.add("anim-previous");

    setTimeout(() => {
        currentOptionImage.style.backgroundImage = "url(" + image_options[i] + ")";
    }, 455);
  
    setTimeout(() => {
        currentOptionText1.innerText = text1_options[i];
        currentOptionText2.innerText = text2_options[i];
        currentOptionPermalink.href = permalink_options[i];
        carousel.classList.remove("anim-previous");
    }, 650);
};
</script>';

return $data;

CSS Code

And here is the CSS.

#carousel-wrapper,
#carousel-wrapper #menu {
    width: 100%;

    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
}

#carousel-wrapper {
    position: relative;
    height: auto;
}
#carousel-wrapper #menu {
    height: 380px;
    overflow: hidden;
    vertical-align: middle;
    box-shadow: 0px 0px 12px 0px rgb(0 0 0 / 20%);
    border-radius: 5px;

    transition: all 0.6s ease-in-out;
}
#carousel-wrapper #menu #current-option {
    position: relative;
    width: 50%;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    overflow: hidden;
}
#carousel-wrapper #menu #current-option a {
    color: var(--primary_colour);
}

#carousel-wrapper #menu #current-option #current-option-text1,
#carousel-wrapper #menu #current-option #current-option-text2 {
    width: 240px;

    display: flex;
    flex-direction: column;
    align-items: flex-start;
}
#carousel-wrapper #menu #current-option #current-option-text1 {
    font-size: 24px;
    font-weight: 700;
    line-height: 1.35;
    height: 200px;
    justify-content: flex-start;
}
#carousel-wrapper #menu #current-option #current-option-text2 {
    font-size: 0.8rem;
    height: 40px;
    justify-content: flex-end;
}

#carousel-wrapper #menu #current-option #current-option-text1::before,
#carousel-wrapper #menu #current-option #current-option-text1::after {
    position: absolute;
    width: 240px;
    height: 200px;

    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-start;
}
#carousel-wrapper #menu #current-option #current-option-text1::before {
    content: attr(data-next-text);
    transform: translate(0%, 380px);
}
#carousel-wrapper #menu #current-option #current-option-text1::after {
    content: attr(data-previous-text);
    transform: translate(0%, -380px);
}

#carousel-wrapper #menu #current-option #current-option-text2::before,
#carousel-wrapper #menu #current-option #current-option-text2::after {
    position: absolute;
    width: 240px;
    height: 40px;

    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-end;
}
#carousel-wrapper #menu #current-option #current-option-text2::before {
    content: attr(data-next-text);
    transform: translate(0%, 380px);
}
#carousel-wrapper #menu #current-option #current-option-text2::after {
    content: attr(data-previous-text);
    transform: translate(0%, -380px);
}

#carousel-wrapper #previous-option,
#carousel-wrapper #next-option {
    width: 1.5rem;
    height: 1.5rem;
    border: none;
    display: block;
    cursor: pointer;
    background: url("data:image/svg+xml,%3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 256 256'%3E%3Cpolygon points='225.813,48.907 128,146.72 30.187,48.907 0,79.093 128,207.093 256,79.093' fill='%23333'%3E%3C/polygon%3E%3C/svg%3E");
    background-size: cover;
    position: absolute;
    right: 0;
}
#carousel-wrapper #previous-option {
    transform: scale(1.2) translate(10px, 24px);
}
#carousel-wrapper #next-option {
    transform: scale(1.2) translate(10px, -24px) rotate(180deg);
}

#carousel-wrapper #image {
    height: 240px;
    width: 240px;

    border-radius: 5px;

    background-position: center;
    background-repeat: no-repeat;
    background-size: cover;
}

#carousel-wrapper.anim-previous,
#carousel-wrapper.anim-next {
    pointer-events: none;
}
#carousel-wrapper.anim-next #current-option-text1,
#carousel-wrapper.anim-next #current-option-text2 {
    animation: next-text 0.65s 0.085s;
}
#carousel-wrapper.anim-previous #image,
#carousel-wrapper.anim-next #image {
    animation: previous-next-image 0.65s 0.085s;
}

#carousel-wrapper.anim-previous #current-option-text1,
#carousel-wrapper.anim-previous #current-option-text2 {
    animation: previous-text 0.65s 0.085s;
}

@keyframes previous-text {
    50%, 55% {
        transform: translate(0%, 390px);
    }
    to {
        transform: translate(0%, 380px);
    }
}
@keyframes previous-next-image {
    0% {
        transform: scale(1);
        opacity: 1;
    }
    70% {
        transform: scale(1.1);
        opacity: 0;
    }
    100% {
        transform: scale(1);
        opacity: 1;
    }
}
@keyframes next-text {
    50%, 55% {
        transform: translate(0%, -390px);
    }
    to {
        transform: translate(0%, -380px);
    }
}

@media only screen and (max-width: 480px) {
    #carousel-wrapper #menu {
        height: 430px;
    }
    #carousel-wrapper, #carousel-wrapper #menu {
        flex-direction: column;
    }
    #carousel-wrapper #menu #current-option {
        width: 240px;
    }
}

That’s all there is. If you can’t see the demo above, here’s a screenshot.

Find more JavaScript tutorials, code snippets and samples here or more jQuery tutorials, code snippets and samples here.

👋 Added by Ciprian on Tuesday, April 27, 2021 in Blog, JavaScript. Last modified on Sunday, April 25, 2021.

Leave a Reply

You have to agree to the comment policy.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Privacy Policy