简体   繁体   中英

How to convert this Javascript slider to an OOP solution?

Before explaining the problem, here is a little of context. School wanted us to build from scratch a One Page web resume using only HTML/CSS, Javascript and JQuery.

For that project, I had a lot of ideas. One of them was to use a quote slider. Each x seconds it slides to the next quote. So I went looking for something that would work and I found this and adapted it to make it look like:

 slideIndex = 1; function plusSlides(n) { showSlides(slideIndex += n); } function currentSlide(n) { showSlides(slideIndex = n); } function showSlides(n) { var i; var slides = document.getElementsByClassName("slide"); var dots = document.getElementsByClassName("dot"); if (n > slides.length) { slideIndex = 1; } if (n < 1) { slideIndex = slides.length; } for (i = 0; i < slides.length; i++) { slides[i].style.display = "none"; } for (i = 0; i < dots.length; i++) { dots[i].className = dots[i].className.replace(" activeDot", ""); } slides[slideIndex - 1].style.display = "block"; dots[slideIndex - 1].className += " activeDot"; } $(document).ready(function() { showSlides(slideIndex); setInterval(() => { plusSlides(1); }, 5000); });
 .sliderContainer { position: relative; background: #eee; /* Not useful here */ }.slide { display: none; padding: 30px; text-align: center; }.prev, .next { cursor: pointer; position: absolute; top: 50%; width: auto; margin-top: -30px; padding: 16px; color: #444; font-weight: bold; font-size: 20px; border-radius: 0 3px 3px 0; user-select: none; }.next { position: absolute; right: 0; border-radius: 3px 0 0 3px; }.prev:hover, .next:hover { /* Changes color and background color */ }.dotContainer { margin-bottom: 30px; text-align: center; padding: 20px; }.dot { cursor: pointer; height: 15px; width: 15px; margin: 0 20px; background-color: #444; /* still not useful but I let it */ border-radius: 50%; display: inline-block; transition: background-color 0.6s ease; }.activeDot, .dot:hover { /* changes background-color */ } q { /* changes font-style */ }.author { /* changes color */ }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-color/2.1.2/jquery.color.min.js"></script> <div class="sliderContainer"> <div class="slide"> <q>A quote</q> <p class="author">The quote's author</p> </div> <div class="slide"> <q>Another quote</q> <p class="author">The quote's other author</p> </div> <;-- Left and Right buttons --> <a class="prev" onclick="plusSlides(-1)">&#10094;</a> <a class="next" onclick="plusSlides(1)">&#10095;</a> </div> <div class="dotContainer"> <span class="dot" onclick="currentSlide(1)"></span> <span class="dot" onclick="currentSlide(2)"></span> </div>

So, this code works perfectly fine.

But then I wanted to add another slider somewhere else. It was a figure slider and not a quote slider, but that doesn't change anything. I did build it in the HTML and looked how that work. And it didn't work correctly because all the slides had the same class. Obviously.

Then I tried changing the classes from slide to quoteSlide and figureSlide . Then I realised I needed to change all the JavaScript related to that. Well, for only 2 sliders, it doesn't mind. I just did a copy/paste and changed the name of the functions and other small things to get the quote adapted script and the figure adapted script.

But then, I realised I would need a total of seven sliders. And I wouldn't get seven times the same script. So I thought and I found an interesting idea. In my school cursus, we learn a lot of OOP (Object Oriented Programming). But we never talked about it in Javascript class. So I said to myself, why not trying to do this.

This wasn't the simplest thing to learn, but I managed to get something. Spoiler: this thing didn't work. But here it is:

class Slider {

    slideIndex = 1;
    slides;
    dots;

    constructor(index = 1, slides, dots) {
        slideIndex = index;
        slides = document.getElementsByClassName(slides);
        dots = document.getElementsByClassName(dots);
    }

    plusSlides(n) {
        showSlides(slideIndex += n);
    }

    currentSlide(n) {
        showSlides(slideIndex = n);
    }

    showSlides(n) {
        var i;

        if (n > slides.length) { slideIndex = 1; }

        if (n < 1) { slideIndex = slides.length; }

        for(i = 0; i < slides.length; i++) {
            slides[i].style.display = "none";
        }

        for(i = 0; i < dots.length; i++) {
            dots[i].className = dots[i].className.replace(" activeDot", "");
        }

        slides[slideIndex - 1].style.display = "block";
        dots[slideIndex - 1].className += " activeDot";
    }

    setupSlider() {
        showSlides(slideIndex);

        // Sliding automation
        setInterval(() => {
            plusSlides(1);
            console.log(this.slideIndex); 
        }, 5000);
    }
}

And here is the code in the main JS file:

let quoteSlider     = new Slider(1, ".quoteSlide",      ".quoteDot");
let lectureSlider   = new Slider(1, ".lectureSlide",    ".lectureDot");
let gameSlider      = new Slider(1, ".gameSlide",       ".gameDot");
let softwareSlider  = new Slider(1, ".softwareSlide",   ".softwareDot");
let roboticSlider   = new Slider(1, ".roboticSlide",    ".roboticDot");
let teachingSlider  = new Slider(1, ".teachingSlide",   ".teachingDot");
let streamingSlider = new Slider(1, ".streamingSlide",  ".streamingDot");

quoteSlider.setupSlider();
lectureSlider.setupSlider();
gameSlider.setupSlider();
softwareSlider.setupSlider();
roboticSlider.setupSlider();
teachingSlider.setupSlider();
streamingSlider.setupSlider();

This code doesn't work, but I can't understand why.

So here are my questions:

  • Did I made an error in my thinking process? Which one?
  • Where in all this JS stuff I made a mistake?
  • What would have been the solution you'd gave to the teacher knowing the context?
  • Is there something to do better globally?

Disclaimer: This project is over. I waited on purpose for the end of the time-limit to ask as I didn't want to use other people's code to get points for this class.

Thanks for your help, and sorry for that so long text.

Your thinking process is good about making a single class object for this.

First mistake is that everything that defined within root of the class can be accessed within the class by using this. prefix, but you were trying access it by their name only, strangely the only place you used it correctly besides the constructor is in the console.log(this.slideIndex)

There is no way of stopping the automated slides - that could be improved. Also, in some situations it's better provide slides content via javascript rather than hard code into HTML, use HTML as a template instead.

 class Slider { slideIndex = 1; slides; dots; constructor(index = 1, sel) { this.slideIndex = index; const container = document.querySelector(sel); this.slides = container.querySelectorAll(".slide"); const dots = container.querySelector(".dotContainer"); dots.innerHTML = ""; this.dots = []; for(let i = 0; i < this.slides.length; i++) { const dot = document.createElement("span"); dot.className = "dot"; dot.addEventListener("click", e => this.currentSlide(i + 1)); dots.appendChild(dot); this.dots[i] = dot; } const nav = container.querySelector(".nav"); nav.innerHTML = ""; const prev = document.createElement("a"); prev.className = "prev"; prev.addEventListener("click", e => this.plusSlides(-1)); prev.innerHTML = "&#10094;"; nav.appendChild(prev); const next = document.createElement("a"); next.className = "next"; next.addEventListener("click", e => this.plusSlides(-1)); next.innerHTML = "&#10095;"; nav.appendChild(next); } plusSlides(n) { this.showSlides(this.slideIndex += n); } currentSlide(n) { this.showSlides(this.slideIndex = n); } showSlides(n) { var i; if (n > this.slides.length) { this.slideIndex = 1; } if (n < 1) { this.slideIndex = this.slides.length; } for (i = 0; i < this.slides.length; i++) { this.slides[i].style.display = i == this.slideIndex - 1? "block": "none"; } for (i = 0; i < this.dots.length; i++) { this.dots[i].classList.toggle("activeDot", i == this.slideIndex-1); } } timer = null; stopSlider() { clearInterval(this.timer); } setupSlider(speed) { this.showSlides(this.slideIndex); if (speed === undefined) speed = 5000; // Sliding automation clearInterval(this.timer); this.timer = setInterval(() => { this.plusSlides(1); console.log(this.slideIndex); }, speed); } } let quoteSlider = new Slider(1, ".quoteSlide"); quoteSlider.setupSlider(1000); /* let lectureSlider = new Slider(1, ".lectureSlide", ".lectureDot"); let gameSlider = new Slider(1, ".gameSlide", ".gameDot"); let softwareSlider = new Slider(1, ".softwareSlide", ".softwareDot"); let roboticSlider = new Slider(1, ".roboticSlide", ".roboticDot"); let teachingSlider = new Slider(1, ".teachingSlide", ".teachingDot"); let streamingSlider = new Slider(1, ".streamingSlide", ".streamingDot"); lectureSlider.setupSlider(); gameSlider.setupSlider(); softwareSlider.setupSlider(); roboticSlider.setupSlider(); teachingSlider.setupSlider(); streamingSlider.setupSlider(); */
 .sliderContainer { position: relative; background: #eee; /* Not useful here */ }.slide { display: none; padding: 30px; text-align: center; }.prev, .next { cursor: pointer; position: absolute; top: 50%; width: auto; margin-top: -30px; padding: 16px; color: #444; font-weight: bold; font-size: 20px; border-radius: 0 3px 3px 0; user-select: none; }.next { position: absolute; right: 0; border-radius: 3px 0 0 3px; }.prev:hover, .next:hover { /* Changes color and background color */ }.dotContainer { margin-bottom: 30px; text-align: center; padding: 20px; }.dot { cursor: pointer; height: 15px; width: 15px; margin: 0 20px; background-color: #444; /* still not useful but I let it */ border-radius: 50%; display: inline-block; transition: background-color 0.6s ease; }.activeDot, .dot:hover { /* changes background-color */ } q { /* changes font-style */ }.author { /* changes color */ }
 <!-- Unrelated content --> <div class="quoteSlide"> <div class="sliderContainer"> <div class="slide"> <q>A quote</q> <p class="author">The quote's author</p> </div> <div class="slide"> <q>Another quote</q> <p class="author">The quote's author</p> </div> <!-- Left and Right buttons --> <div class="nav"> </div> </div> <div class="dotContainer"> </div> </div>

Your approach is very good and i found only three issues:

  • You have to call vars and functions of that class inside of it with this. for "saying" in which context the var or function is used.
  • You don't need to declare the three vars slideIndex , slides and dots at the beginning because this is already done in the constructor.
  • The inline event listeners don't work with that OOP structure. Therefor you should add them to the function setupSlider() .

Because the usage of a container class is the easiest way for selecting the .prev - and .next -buttons in the setup function, you should give it to the constructor, for example: let quoteSlider = new Slider(1, ".quotes"); . You can omit the specific classes, like .quoteSlide or .quoteDot , if you simply add in the constructor '.slide' or '.dot' to the container class in the selector. Of course you have to give that container class to the containers, for example: class="sliderContainer quotes" and class="dotContainer quotes" .

Working example:

 class Slider { constructor(index = 1, container) { this.slideIndex = index; this.container = container; this.slides = document.querySelectorAll(container + ".slide"); this.dots = document.querySelectorAll(container + ".dot"); } plusSlides(n) { this.showSlides(this.slideIndex += n); } currentSlide(n) { this.showSlides(this.slideIndex = n); } showSlides(n) { var i; if (n > this.slides.length) { this.slideIndex = 1; } if (n < 1) { this.slideIndex = this.slides.length; } for (i = 0; i < this.slides.length; i++) { this.slides[i].style.display = "none"; } for (i = 0; i < this.dots.length; i++) { this.dots[i].className = this.dots[i].className.replace(" activeDot", ""); } this.slides[this.slideIndex - 1].style.display = "block"; this.dots[this.slideIndex - 1].className += " activeDot"; } setupSlider() { document.querySelector(this.container + ".prev").addEventListener("click", e => this.plusSlides(-1) ); document.querySelector(this.container + ".next").addEventListener("click", e => this.plusSlides(1) ); for (let i = 0; i < this.dots.length; i++) { this.dots[i].addEventListener("click", e => this.currentSlide(i + 1)); } this.showSlides(this.slideIndex); // Sliding automation setInterval(() => { this.plusSlides(1); }, 5000); } } $(document).ready(function() { let quoteSlider = new Slider(1, ".quotes"); let lectureSlider = new Slider(1, ".lectures"); let gameSlider = new Slider(1, ".games"); let softwareSlider = new Slider(1, ".software"); let roboticSlider = new Slider(1, ".robotics"); let teachingSlider = new Slider(1, ".teachings"); let streamingSlider = new Slider(1, ".streaming"); quoteSlider.setupSlider(); lectureSlider.setupSlider(); gameSlider.setupSlider(); softwareSlider.setupSlider(); roboticSlider.setupSlider(); teachingSlider.setupSlider(); streamingSlider.setupSlider(); });
 .sliderContainer { position: relative; background: #eee; /* Not useful here */ }.slide { display: none; padding: 30px; text-align: center; }.prev, .next { cursor: pointer; position: absolute; top: 50%; width: auto; margin-top: -30px; padding: 16px; color: #444; font-weight: bold; font-size: 20px; border-radius: 0 3px 3px 0; user-select: none; }.next { position: absolute; right: 0; border-radius: 3px 0 0 3px; }.prev:hover, .next:hover { /* Changes color and background color */ }.dotContainer { margin-bottom: 30px; text-align: center; padding: 20px; }.dot { cursor: pointer; height: 15px; width: 15px; margin: 0 20px; background-color: #444; /* still not useful but I let it */ border-radius: 50%; display: inline-block; transition: background-color 0.6s ease; }.activeDot, .dot:hover { /* changes background-color */ } q { /* changes font-style */ }.author { /* changes color */ }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-color/2.1.2/jquery.color.min.js"></script> <div class="sliderContainer quotes"> <div class="slide"> <q>A quote</q> <p class="author">The quote's author</p> </div> <div class="slide"> <q>Another quote</q> <p class="author">The quote's other author</p> </div> <;-- Left and Right buttons --> <a class="prev">&#10094;</a> <a class="next">&#10095;</a> </div> <div class="dotContainer quotes"> <span class="dot"></span> <span class="dot"></span> </div> <div class="sliderContainer lectures"> <div class="slide"> <q>A lecture</q> <p class="author">The lecture's author</p> </div> <div class="slide"> <q>Another lecture</q> <p class="author">The lecture's other author</p> </div> <;-- Left and Right buttons --> <a class="prev">&#10094;</a> <a class="next">&#10095;</a> </div> <div class="dotContainer lectures"> <span class="dot"></span> <span class="dot"></span> </div> <div class="sliderContainer games"> <div class="slide"> <q>A game</q> <p class="author">The game's author</p> </div> <div class="slide"> <q>Another game</q> <p class="author">The game's other author</p> </div> <;-- Left and Right buttons --> <a class="prev">&#10094;</a> <a class="next">&#10095;</a> </div> <div class="dotContainer games"> <span class="dot"></span> <span class="dot"></span> </div> <div class="sliderContainer software"> <div class="slide"> <q>A software</q> <p class="author">The software's author</p> </div> <div class="slide"> <q>Another software</q> <p class="author">The software's other author</p> </div> <;-- Left and Right buttons --> <a class="prev">&#10094;</a> <a class="next">&#10095;</a> </div> <div class="dotContainer software"> <span class="dot"></span> <span class="dot"></span> </div> <div class="sliderContainer robotics"> <div class="slide"> <q>A robotic</q> <p class="author">The robotic's author</p> </div> <div class="slide"> <q>Another robotic</q> <p class="author">The robotic's other author</p> </div> <;-- Left and Right buttons --> <a class="prev">&#10094;</a> <a class="next">&#10095;</a> </div> <div class="dotContainer robotics"> <span class="dot"></span> <span class="dot"></span> </div> <div class="sliderContainer teachings"> <div class="slide"> <q>A teaching</q> <p class="author">The teaching's author</p> </div> <div class="slide"> <q>Another teaching</q> <p class="author">The teaching's other author</p> </div> <!-- Left and Right buttons --> <a class="prev">&#10094;</a> <a class="next">&#10095;</a> </div> <div class="dotContainer teachings"> <span class="dot"></span> <span class="dot"></span> </div> <div class="sliderContainer streaming"> <div class="slide"> <q>A streaming</q> <p class="author">The streaming's author</p> </div> <div class="slide"> <q>Another streaming</q> <p class="author">The streaming's other author</p> </div> <!-- Left and Right buttons --> <a class="prev">&#10094;</a> <a class="next">&#10095;</a> </div> <div class="dotContainer streaming"> <span class="dot"></span> <span class="dot"></span> </div>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM