Hvordan man bygger en karrusel fra bunden af ​​vanilje JS.

Foto af Xavi Cabrera på Unsplash

Tempoet for ny teknologi, der frigives, er utroligt. Så hurtigt, at det er let at hoppe på den nyeste og største ramme eller bibliotek uden at lægge tid til virkelig at forstå de grundlæggende.

Efter at have administreret og i øjeblikket administrere webudviklingsteam, er mit hovedfokus den personlige vækst i mit team. En af de første opgaver, jeg satte til spirende udviklere, der er ivrige efter at udvikle deres basale JS-viden, er at opbygge en karrusel.

Carousel Challenge

Uret er klar.

Byg en fungerende karrusel i browserlæsbar kode. Ingen biblioteker, ingen rammer, ingen bygningsværktøjer. Bare HTML5, CSS3 og JavaScript.
Bonuspoint, hvis det fungerer med berøringshændelser på berøringsskærmsenheder.

Årsagen til at jeg elsker at sætte denne opgave er fordi opbygning af en karrusel kræver arkitektonisk tænkning, styring af data og elementer, DOM-manipulation og hensyntagen til brugerinteraktion. Der er så mange måder at henvende sig til og lære af denne opgave, at den samme udvikler, der gør denne samme brief, med et par måneder imellem, ville føre til helt andre resultater.

Nu, nok til at tale, lad os begynde at finde ud af dette.

Inden der skrives nogen kode, skal vi beslutte, hvordan vi vil have HTML, CSS og JS til at arbejde sammen.

Skal vi videregive billederne som en matrix til JS, skal vi bygge den semantiske HTML, så strukturen er der, før JS indlæses, vil der være animation, hvis det er tilfældet, vil det blive drevet af JS eller CSS?

Dette eksempel vil bruge struktureret HTML, CSS3-overgange og interaktivitet kontrolleret af JS.

HTML: struktur

Vi vil opbygge strukturen til vores karrusel. Når vi koder dette, er vi nødt til at overveje, at der muligvis er mere end en karrusel på en enkelt side, så af hensyn til genanvendelighed bruger vi klasseattributten over id til målretning og styling.

Her er vores grundlæggende struktur:

    
    
    
    
    
      
    
    
  

Fra karusellindpakningen bruger vi denne til at dimensionere vores karrusel og skjule alt oversvømmelsesindhold inde i det.

karrusel vil indeholde alle vores elementer som billeder og navigationsknapper.

carousel__photo anvendes til vores tags for nem styling. Vi har også givet det billede, vi oprindeligt ønsker at vise en klasse af initial.

karruselknap med - næste og - forudgående flag så vi kan give hver af dem deres egen klikbegivenhed. For dem, der er interesseret i mine klassekonventioner, følger jeg BEM-metodologien.

HTML udført. Dernæst CSS.

CSS: Stilarter og overgange

Jeg er en stor fortaler for at skrive CSS til mindre skærme først - AKA Mobile First) og derefter bruge medieforespørgsler til at udvide designet, som indholdet dikterer.

Med det i tankerne vil denne CSS være en enkelt-søjlevisning af karrusellen, og for kortfattethedens skyld vil jeg udelade leverandørpræfikser (f.eks.-webkit-).

Lad os gøre det.

Dykker ind med .carousel-indpakning, intet specielt her, bare overløb og bredde. Ideen her er, at bredden kan ændres, så den passer til dit indhold, og alt indeni det skalerer for at passe.

.carousel-indpakning {
  overløb: skjult;
  bredde: 90%;
}

Vi ønsker også at anvende border-box på egenskaben box-dimensionering, så enhver polstring og kant er inkluderet i vores elementers samlede bredde og højde.

.carousel-indpakning * {
  box-dimensionering: border-box;
}

Vi bruger transform-egenskaben til at flytte vores karrusels genstande, så indstilling af transformestilen til at bevare-3d vil sikre, at vores indlejrede elementer gengives korrekt i 3D-rum.

.karrusel {
  transform-stil: preserve-3d;
}

Som standard skjuler vi alle elementer, indtil scriptet starter karrusellen. For nem placering vil elementer være absolut placeret og få en responsiv bredde på 100%. Vores indhold dikterer karusellens højde, og overgangsejendommen vil være vores magi til at gøre karusellen animeret.

.carousel__photo {
  opacitet: 0;
  position: absolut;
  top: 0;
  bredde: 100%;
  margin: auto;
  polstring: 1rem 4rem;
  z-indeks: 100;
  overgang: transformere .5s, opacitet .5s, z-index .5s;
}

Nogle gange kan scripts tage lidt tid at indlæse, så lad os vise den indledende, gøre den relativ, så overordnet beholder udvides og bringe den til fronten ved hjælp af z-indeks. Disse stilarter gælder også for vores aktive vare, når karrusellen kontrolleres.

.carousel__photo.initial,
.carousel__photo.active {
  opacitet: 1;
  position: relativ;
  z-indeks: 900;
}

Når vi navigerer gennem karruslerne, ønsker vi, at JS dynamisk indstiller klasser til at forpositionere de forrige og næste poster ved at oversætte dem med transform-egenskaben. Vi bruger z-indeks igen til at placere disse oven på andre elementer, men under det aktive emne.

.carousel__photo.prev,
.carousel__photo.næste {
  z-indeks: 800;
}
.carousel__photo.prev {
  transform: translateX (-100%); / * Flyt 'forrige' element til venstre * /
}
.carousel__photo.næste {
  transform: translateX (100%); / * Flyt 'næste' element til højre * /
}

CSS til karrusellen er færdig, alt, hvad der er tilbage, er navigationsknapperne, der sidder i midten, hver side af karrusellen med pilene indeni. I stedet for at tilføje mere HTML, tilføjer vi pilene ved hjælp af :: efter pseudo-elementet.

.carousel__button - forrige,
.carousel__-knap - næste {
  position: absolut;
  top: 50%;
  bredde: 3rem;
  højde: 3rem;
  baggrundsfarve: #FFF;
  transform: translateY (-50%);
  grænseradius: 50%;
  markør: markør;
  z-indeks: 1001; / * Sid oven på alt * /
  kant: 1px massiv sort;
}
.carousel__-knap - forrige {
  venstre: 0;
}
.carousel__-knap - næste {
  højre: 0;
}
.carousel__button - prev :: efter,
.carousel__button - næste :: efter {
  indhold: " ";
  position: absolut;
  bredde: 10px;
  højde: 10px;
  top: 50%;
  venstre: 54%;
  kant til højre: 2px massiv sort;
  kant-bund: 2px massiv sort;
  transform: oversætte (-50%, -50%) rotere (135deg);
}
.carousel__button - næste :: efter {
  venstre: 47%;
  transform: oversætte (-50%, -50%) rotere (-45deg);
}
Hvis du har fulgt med, skal din karrusel ikke se for ulig ud over dette.

JavaScript: Gør det til at fungere!

Hvis du har nået det så langt, godt klaret, og tak. Virkelig. Mange tak. Du er den bedste. ❤

Vi har strukturen, vi har den set ud, hvordan vi vil med overgange, der er raring til at gå, nu skal vi bare gøre det arbejde.

Tid til at finde ud af dette, inden vi begynder at kode.

  1. Vi er nødt til at indlede karrusellen ved at finde den oprindelige vare og anvende klassen .prev og .næste på dens tilstødende genstande.
  2. Derefter vil vi tilføje klikbegivenheder til vores navigationsknapper.
  3. Klikbegivenheder er intet uden en funktion, så vi skriver to funktioner til at håndtere hver retning.
  4. Når vi ved, hvilken retning en bruger forsøger at navigere, lad os skrive en anden funktion til at flytte karrusellen i den retning.
  5. For at undgå, at folk over-klikker på knapperne, deaktiverer vi interaktivitet, mens karrusellen animerer, og aktiverer den igen, når den er færdig.
  6. Til sidst vil vi håndtere alt dette i en funktion, der flytter elementer i vores karrusel ved at finde ud af, hvilke poster der skal opdateres, og opdatere dem med nye klasser for at udløse CSS3-overgange.

For at undgå konflikter og gøre dette så bærbart som muligt er det i vores bedste interesse at beskytte vores kode mod det globale omfang, så vi sammenpakker det i en IIFE.

! (Funktion (d) {
  // Alle koder vil gå ind her. Vi har omdøbt 'dokument' til 'd'.
}(dokument));

For at begynde med erklærer vi vores variabler. Vi vil indstille en variabel til at målrette vores baseklasse .carousel__foto, derefter gemme alle objekter med denne klasse i emner, når de først er gemt, kan vi tælle dem og gemme det antal i totalItems, så indstiller vi dias til at være vores nuværende dias (ved indeks 0), og for at afslutte tildeler vi bevægelse som sandt, som vi vil bruge til at aktivere og deaktivere knapklik.

var itemClassName = "carousel__photo";
    items = d.getElementsByClassName (itemClassName),
    totalItems = items.length,
    dias = 0,
    bevægelse = sand;

Disse næste to funktioner indstiller de første klasser og tilføjer vores begivenhedslyttere til navigationsknapperne.

// Indstil klasser
funktion setInitialClasses () {
  // Målretter de foregående, aktuelle og næste poster
  // Dette antager, at der er mindst tre poster.
  varer [totalItems - 1] .classList.add ("forrige");
  elementer [0] .classList.add ( "aktivt");
  elementer [1] .classList.add ( "næste");
}
// Indstil begivenhedslyttere
funktion setEventListeners () {
  var næste = d.getElementsByClassName ('karruselknap - næste') [0],
      prev = d.getElementsByClassName ('karruselknap - forrige') [0];
  next.addEventListener ('klik', flytNæste);
  prev.addEventListener ('klik', flyt forud);
}

Vi binder flytNæste og flyt Forud til klikbegivenheden, så vi opretter bedre disse funktioner næste. Disse funktioner kontrollerer, hvad det aktuelle diasnummer er, og enten øges, nedbrydes eller indstilles til det første eller sidste punkt.

// Næste navigationsbehandler
funktion moveNext () {
  // Kontroller, om du bevæger dig
  hvis (! flytter) {
    // Hvis det er den sidste dias, skal du nulstille til 0, ellers +1
    if (dias === (totalItems - 1)) {
      dias = 0;
    } andet {
      glide ++;
    }
    // Flyt karrusellen til det opdaterede dias
    moveCarouselTo (slide);
  }
}
// Tidligere navigationsbehandler
funktion movePrev () {
  // Kontroller, om du bevæger dig
  hvis (! flytter) {
    // Hvis det er den første dias, skal du indstille som den sidste dias, ellers -1
    if (dias === 0) {
      slide = (totalItems - 1);
    } andet {
      glide--;
    }
          
    // Flyt karrusellen til det opdaterede dias
    moveCarouselTo (slide);
  }
}

Fantastisk. Så vi er i stand til at køre koden, lad os skrive en lille logik til at håndtere den flytende variabel, der sætter den til sand, når den udløses, og derefter tilbage til falsk, når vores overgang er afsluttet.

function disableInteraction () {
  // Sæt 'bevægelse' til sand i samme varighed som vores overgang.
  // (0,5s = 500ms)
  
  bevægelse = sand;
  // setTimeout kører sin funktion en gang efter det givne tidspunkt
  setTimeout (function () {
    bevægelse = falsk
  }, 500);
}

Den vigtigste funktion, der håndterer hele karrusellen, sidder inden for moveCarouselTo (dias), der tager et diasnummer som et argument. Dette er vores største funktion, så jeg har kommenteret koden.

funktion moveCarouselTo (dias) {
  // Kontroller, om karrusellen bevæger sig, hvis ikke, tillad interaktion
  hvis (! flytter) {
    // deaktiver interaktivitet midlertidigt
    disableInteraction ();
    // Opdater de "gamle" tilstødende lysbilleder med "nye"
    var newPrevious = dias - 1,
        newNext = dias + 1,
        oldPrevious = dias - 2,
        oldNext = slide + 2;
    // Test om karrusellen har mere end tre genstande
    if ((totalItems - 1)> 3) {
      // Kontrollerer og opdaterer, om de nye slides er uden for grænserne
      if (newPrevious <= 0) {
        oldPrevious = (totalItems - 1);
      } andet hvis (newNext> = (totalItems - 1)) {
        oldNext = 0;
      }
      // Kontrollerer og opdaterer, hvis dias er i begyndelsen / slutningen
      if (dias === 0) {
        newPrevious = (totalItems - 1);
        oldPrevious = (totalItems - 2);
        oldNext = (dias + 1);
      } andet hvis (dias === (totalItems -1)) {
        newPrevious = (dias - 1);
        nytNæste = 0;
        oldNext = 1;
      }
      // Nu har vi fundet ud af, hvor vi er, og hvor vi skal hen,
      // ved at tilføje / fjerne klasser udløser vi overgange.
      // Nulstil gamle næste / forrige elementer til standardklasser
      elementer [oldPrevious] .className = itemClassName;
      items [oldNext] .className = itemClassName;
      // Tilføj nye klasser
      elementer [newPrevious] .className = itemClassName + "prev";
      items [slide] .className = itemClassName + "aktiv";
      elementer [newNext] .className = itemClassName + "næste";
    }
  }
}

Vi er næsten der! Der er en sidste funktion, vi er nødt til at udføre, og det er den, vi kalder for at få alt til at fungere.

funktion initCarousel () {
  setInitialClasses ();
  setEventListeners ();
  // Indstil flytning til falsk, så karrusellen bliver interaktiv
  bevægelse = falsk;
}

Til sidst lad os ringe til initCarousel () ved at tilføje det i bunden:

// få det til at regne
initCarousel ();

Hvis du har fulgt med, skal du se - og interagere - med noget som dette:

Jeg håber, du har haft glæde af min debutartikel. Hvis du har fundet det nyttigt, skal du dele det, og hvis du vil holde dig opdateret, kan du følge mig på Instagram.