javascripthtmlcsssimplify

How to simplify this simple JS script?


I'd like to know how I could make the code shorter by not repeating everything basically 4 times (each for every individual .section1-item). I'd like to know if there is a syntax or something that could make the code look cleaner and most of all not so redundant. I'm very new to JS and I'd like to learn more about it, and how I could make it more optimised.

const section1Card1 = document.querySelector(
    ".section1-col:nth-of-type(1) .section1-item:nth-child(1)"
);
const section1Card2 = document.querySelector(
    ".section1-col:nth-of-type(1) .section1-item:nth-child(2)"
);
const section1Card3 = document.querySelector(
    ".section1-col:nth-of-type(3) .section1-item:nth-child(1)"
);
const section1Card4 = document.querySelector(
    ".section1-col:nth-of-type(3) .section1-item:nth-child(2)"
);
//
const section1Card1Img = document.querySelector(
    ".section1-col:nth-of-type(1) .section1-item:nth-child(1) img"
);
const section1Card2Img = document.querySelector(
    ".section1-col:nth-of-type(1) .section1-item:nth-child(2) img"
);
const section1Card3Img = document.querySelector(
    ".section1-col:nth-of-type(3) .section1-item:nth-child(1) img"
);
const section1Card4Img = document.querySelector(
    ".section1-col:nth-of-type(3) .section1-item:nth-child(2) img"
);
//
const cardIndexDot1 = document.querySelector(".image-index-dot:nth-of-type(1)");
const cardIndexDot2 = document.querySelector(".image-index-dot:nth-of-type(2)");
const cardIndexDot3 = document.querySelector(".image-index-dot:nth-of-type(3)");
const cardIndexDot4 = document.querySelector(".image-index-dot:nth-of-type(4)");
//
const section1Cards = document.querySelectorAll(".section1-item");
const section1CardsImg = document.querySelectorAll(".section1-item img");

section1Card1.addEventListener("click", () => {
    console.log("bon");
    if (!section1Card1.classList.contains("active-card")) {
        section1Card2.classList.remove("active-card");
        section1Card3.classList.remove("active-card");
        section1Card4.classList.remove("active-card");
        section1CardsImg.src = "images/tcheen-logo-white.png";
        section1Card1.classList.add("active-card");
        section1Card1Img.src = "images/tcheen-logo-jaune.png";
        cardIndexDot1.classList.add("image-index-dot-active");
        cardIndexDot2.classList.remove("image-index-dot-active");
        cardIndexDot3.classList.remove("image-index-dot-active");
        cardIndexDot4.classList.remove("image-index-dot-active");
        console.log("oui");
    }
});
section1Card2.addEventListener("click", () => {
    console.log("bon");
    if (!section1Card2.classList.contains("active-card")) {
        section1Card1.classList.remove("active-card");
        section1Card3.classList.remove("active-card");
        section1Card4.classList.remove("active-card");
        section1CardsImg.src = "images/tcheen-logo-blanc.png";
        section1Card2.classList.add("active-card");
        section1Card2Img.src = "images/tcheen-logo-jaune.png";
        cardIndexDot2.classList.add("image-index-dot-active");
        cardIndexDot1.classList.remove("image-index-dot-active");
        cardIndexDot3.classList.remove("image-index-dot-active");
        cardIndexDot4.classList.remove("image-index-dot-active");
        console.log("oui");
    }
});
section1Card3.addEventListener("click", () => {
    console.log("bon");
    if (!section1Card3.classList.contains("active-card")) {
        section1Card1.classList.remove("active-card");
        section1Card2.classList.remove("active-card");
        section1Card4.classList.remove("active-card");
        section1CardsImg.src = "images/tcheen-logo-blanc.png";
        section1Card3.classList.add("active-card");
        section1Card3Img.src = "images/tcheen-logo-jaune.png";
        cardIndexDot3.classList.add("image-index-dot-active");
        cardIndexDot2.classList.remove("image-index-dot-active");
        cardIndexDot1.classList.remove("image-index-dot-active");
        cardIndexDot4.classList.remove("image-index-dot-active");
        console.log("oui");
    }
});
section1Card4.addEventListener("click", () => {
    console.log("bon");
    if (!section1Card4.classList.contains("active-card")) {
        section1Card1.classList.remove("active-card");
        section1Card2.classList.remove("active-card");
        section1Card3.classList.remove("active-card");
        section1CardsImg.src = "images/tcheen-logo-blanc.png";
        section1Card4.classList.add("active-card");
        section1Card4Img.src = "images/tcheen-logo-jaune.png";
        cardIndexDot4.classList.add("image-index-dot-active");
        cardIndexDot2.classList.remove("image-index-dot-active");
        cardIndexDot3.classList.remove("image-index-dot-active");
        cardIndexDot1.classList.remove("image-index-dot-active");
        console.log("oui");
    }
});
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: "Poppins";
}

:root {
    --color-yellow: #efd858;
    --color-pink: #eebcd8;
    --color-green: #6cbe99;
    --color-black: #1a1a1a;
    --color-white: #ffffff;
}

html {
    scroll-behavior: smooth;
}

/*  */
.yellow-bg {
    background-color: var(--color-yellow);
}

img {
    width: 100%;
}

/*  */
.section1 {
    position: relative;
    display: flex;
    justify-content: center;
    gap: 40px;
    padding-top: 98px;
}

.section1-col {
    width: 20%;
}

.section1-img {
    width: 27%;
}

.section1-img img {
    border-radius: 10px;
}

.section1-item {
    padding: 30px 18px 80px;
    position: relative;
    border-radius: 10px;
    background: var(--color-green);
}

.section1-item:nth-of-type(odd) {
    margin-bottom: 15px;
}

.section1-item img {
    position: absolute;
    display: inline-block;
    top: 18px;
    right: 18px;
    width: 12.5%;
}

.section1-item h3 {
    font-weight: 500;
    font-size: 20px;
    line-height: 26px;
    width: 75%;
}

.section1-item p {
    font-weight: 400;
    font-size: 16px;
    line-height: 26px;
}

.image-index {
    position: absolute;
    bottom: -36px;
    width: 120px;
    display: flex;
    justify-content: space-between;
}

.image-index-dot {
    width: 15px;
    aspect-ratio: 1;
    border-radius: 100%;
    background: var(--color-black);
}

/*  */
.active-card {
    background: var(--color-yellow);
}

.image-index-dot-active {
    background: var(--color-yellow);
}
<section class="section1">
    <div class="section1-col">
        <article class="section1-item active-card">
            <img src="images/tcheen-logo-jaune.png" alt="Tcheen Logo">
            <h3>Eco-responsable et original</h3>
            <p>La crème des prestataires sensibilisés à l’engagement durable.</p>
        </article>
        <article class="section1-item">
            <img src="images/tcheen-logo-blanc.png" alt="Tcheen Logo">
            <h3>Devis instantané</h3>
            <p>Obtenez le match parfait et votre devis en 2 minutes chrono !</p>
        </article>
    </div>
    <div class="section1-img">
        <img src="https://source.unsplash.com/random/390x500/?space,planet" alt="Image 1">
    </div>
    <div class="section1-col">
        <article class="section1-item">
            <img src="images/tcheen-logo-blanc.png" alt="Tcheen Logo">
            <h3>100% Transparent</h3>
            <p>Nos frais de service sont transparents et sans surcoût sur vos prestations.</p>
        </article>
        <article class="section1-item">
            <img src="images/tcheen-logo-blanc.png" alt="Tcheen Logo">
            <h3>Accompagnement personnalisé</h3>
            <p>Nous vous accompagnons de A à Z avant et pendant votre événement.</p>
        </article>
    </div>
    <div class="image-index">
        <div class="image-index-dot image-index-dot-active"></div>
        <div class="image-index-dot"></div>
        <div class="image-index-dot"></div>
        <div class="image-index-dot"></div>
    </div>
</section>


Solution

  • You can boil the JS down to a few lines by taking a more general approach.

    You can find the collection of all those articles by selecting on their class and then stepping through them all adding a click event listener.

    When you execute a listener it knows the index of the item that has been clicked and so can add the active class and relevant logo image and also add the right class to the relevant dot.

    const items = document.querySelectorAll('.section1 > .section1-col .section1-item');
    const dots = document.querySelectorAll('.section1 > .image-index > .image-index-dot');
    for (let i = 0; i < items.length; i++) {
      items[i].addEventListener('click', function () {
        for (let j = 0; j < items.length; j++) {
          items[j].classList.remove('active-card');
          items[j].querySelector('img').src = 'images/tcheen-logo-blanc.png';
          dots[j].classList.remove('image-index-dot-active');
        }
        items[i].classList.add('active-card');
        items[i].querySelector('img').src = 'images/tcheen-logo-jaune.png';
        dots[i].classList.add('image-index-dot-active');
      });
    }
    

    Here's the full snippet which does not alter your CSS or your HTML:

    const items = document.querySelectorAll('.section1 > .section1-col .section1-item');
    const dots = document.querySelectorAll('.section1 > .image-index > .image-index-dot');
    for (let i = 0; i < items.length; i++) {
      items[i].addEventListener('click', function() {
        for (let j = 0; j < items.length; j++) {
          items[j].classList.remove('active-card');
          items[j].querySelector('img').src = 'images/tcheen-logo-blanc.png';
          dots[j].classList.remove('image-index-dot-active');
        }
        items[i].classList.add('active-card');
        items[i].querySelector('img').src = 'images/tcheen-logo-jaune.png';
        dots[i].classList.add('image-index-dot-active');
      });
    }
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
      font-family: "Poppins";
    }
    
    :root {
      --color-yellow: #efd858;
      --color-pink: #eebcd8;
      --color-green: #6cbe99;
      --color-black: #1a1a1a;
      --color-white: #ffffff;
    }
    
    html {
      scroll-behavior: smooth;
    }
    
    
    /*  */
    
    .yellow-bg {
      background-color: var(--color-yellow);
    }
    
    img {
      width: 100%;
    }
    
    
    /*  */
    
    .section1 {
      position: relative;
      display: flex;
      justify-content: center;
      gap: 40px;
      padding-top: 98px;
    }
    
    .section1-col {
      width: 20%;
    }
    
    .section1-img {
      width: 27%;
    }
    
    .section1-img img {
      border-radius: 10px;
    }
    
    .section1-item {
      padding: 30px 18px 80px;
      position: relative;
      border-radius: 10px;
      background: var(--color-green);
    }
    
    .section1-item:nth-of-type(odd) {
      margin-bottom: 15px;
    }
    
    .section1-item img {
      position: absolute;
      display: inline-block;
      top: 18px;
      right: 18px;
      width: 12.5%;
    }
    
    .section1-item h3 {
      font-weight: 500;
      font-size: 20px;
      line-height: 26px;
      width: 75%;
    }
    
    .section1-item p {
      font-weight: 400;
      font-size: 16px;
      line-height: 26px;
    }
    
    .image-index {
      position: absolute;
      bottom: -36px;
      width: 120px;
      display: flex;
      justify-content: space-between;
    }
    
    .image-index-dot {
      width: 15px;
      aspect-ratio: 1;
      border-radius: 100%;
      background: var(--color-black);
    }
    
    
    /*  */
    
    .active-card {
      background: var(--color-yellow);
    }
    
    .image-index-dot-active {
      background: var(--color-yellow);
    }
    <section class="section1">
      <div class="section1-col">
        <article class="section1-item active-card">
          <img src="images/tcheen-logo-jaune.png" alt="Tcheen Logo">
          <h3>Eco-responsable et original</h3>
          <p>La crème des prestataires sensibilisés à l’engagement durable.</p>
        </article>
        <article class="section1-item">
          <img src="images/tcheen-logo-blanc.png" alt="Tcheen Logo">
          <h3>Devis instantané</h3>
          <p>Obtenez le match parfait et votre devis en 2 minutes chrono !</p>
        </article>
      </div>
      <div class="section1-img">
        <img src="https://source.unsplash.com/random/390x500/?space,planet" alt="Image 1">
      </div>
      <div class="section1-col">
        <article class="section1-item">
          <img src="images/tcheen-logo-blanc.png" alt="Tcheen Logo">
          <h3>100% Transparent</h3>
          <p>Nos frais de service sont transparents et sans surcoût sur vos prestations.</p>
        </article>
        <article class="section1-item">
          <img src="images/tcheen-logo-blanc.png" alt="Tcheen Logo">
          <h3>Accompagnement personnalisé</h3>
          <p>Nous vous accompagnons de A à Z avant et pendant votre événement.</p>
        </article>
      </div>
      <div class="image-index">
        <div class="image-index-dot image-index-dot-active"></div>
        <div class="image-index-dot"></div>
        <div class="image-index-dot"></div>
        <div class="image-index-dot"></div>
      </div>
    </section>
    </script>