Slideshow Navigation prev / next funktioniert, wie können die Slides direkt (z.B. nach ID) angesteuert werden?

#1
Hallo zusammen,

nach zwei Tagen wage ich es nun um Hilfe zu fragen: Mein Ziel ist es, die prev / next Navigation des Sliders zu ersetzen, so dass zu den einzelnen Slides direkt navigiert werden kann (z.B. über die ID). Die Navigation soll wie folgt funktionieren:

HTML:
<nav class="thumbnav">
    <a href="#one"><img src="img/thumb1.jpg" alt="" /></a>
    <a href="#two"><img src="img/thumb2.jpg" alt="" /></a>
    <a href="#three"><img src="img/thumb3.jpg" alt="" /></a>
    <a href="#four"><img src="img/thumb4.jpg" alt="" /></a>
    <a href="#five"><img src="img/thumb5.jpg" alt="" /></a>
    <a href="#six"><img src="img/thumb6.jpg" alt="" /></a>
</nav>
Funktioniert bisher, erlaubt aber nur sukzessive Slides:

HTML:
<!-- This works: -->
<nav class="slidenav">
    <button class="slidenav__item slidenav__item--prev">Previous</button>
    <span>/</span>
    <button class="slidenav__item slidenav__item--next">Next</button>
</nav>
Javascript sieht so aus:

Javascript:
/**
 * demo2.js
 * http://www.codrops.com
 *
 * Licensed under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
 *
 * Copyright 2017, Codrops
 * http://www.codrops.com
 */
{
    // From https://davidwalsh.name/javascript-debounce-function.
    function debounce(func, wait, immediate) {
        var timeout;
        return function() {
            var context = this, args = arguments;
            var later = function() {
                timeout = null;
                if (!immediate) func.apply(context, args);
            };
            var callNow = immediate && !timeout;
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
            if (callNow) func.apply(context, args);
        };
    };
    
    class Slideshow {
        constructor(el) {
            this.DOM = {};
            this.DOM.el = el;
            this.settings = {
                animation: {
                    slides: {
                        duration: 600,
                        easing: 'easeOutQuint'
                    },
                    shape: {
                        duration: 300,
                        easing: {in: 'easeOutQuad', out: 'easeOutQuad'}
                    }
                },
                frameFill: '#111'
            }
            this.init();
        }
        init() {
            this.DOM.slides = Array.from(this.DOM.el.querySelectorAll('.slides > .slide'));
            this.slidesTotal = this.DOM.slides.length;
            this.DOM.nav = this.DOM.el.querySelector('.slidenav');
            this.DOM.nextCtrl = this.DOM.nav.querySelector('.slidenav__item--next');
            this.DOM.prevCtrl = this.DOM.nav.querySelector('.slidenav__item--prev');
            this.current = 0;
            this.createFrame();
            this.initEvents();
        }
        createFrame() {
            this.rect = this.DOM.el.getBoundingClientRect();
            this.frameSize = this.rect.width/12;
            this.paths = {
                initial: this.calculatePath('initial'),
                final: this.calculatePath('final')
            };
            this.DOM.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
            this.DOM.svg.setAttribute('class', 'shape');
            this.DOM.svg.setAttribute('width','100%');
            this.DOM.svg.setAttribute('height','100%');
            this.DOM.svg.setAttribute('viewbox',`0 0 ${this.rect.width} ${this.rect.height}`);
            this.DOM.svg.innerHTML = `<path fill="${this.settings.frameFill}" d="${this.paths.initial}"/>`;
            this.DOM.el.insertBefore(this.DOM.svg, this.DOM.nav);
            this.DOM.shape = this.DOM.svg.lastElementChild;
        }
        updateFrame() {
            this.paths.initial = this.calculatePath('initial');
            this.paths.final = this.calculatePath('final');
            this.DOM.svg.setAttribute('viewbox',`0 0 ${this.rect.width} ${this.rect.height}`);
            this.DOM.shape.setAttribute('d', this.isAnimating ? this.paths.final : this.paths.initial);
        }
        calculatePath(path = 'initial') {
            if ( path === 'initial' ) {
                return `M 0,0 0,${this.rect.height} ${this.rect.width},${this.rect.height} ${this.rect.width},0 0,0 Z M 0,0 ${this.rect.width},0 ${this.rect.width},${this.rect.height} 0,${this.rect.height} Z`;
            }
            else {
                return {
                    next: `M 0,0 0,${this.rect.height} ${this.rect.width},${this.rect.height} ${this.rect.width},0 0,0 Z M ${this.frameSize},${this.frameSize} ${this.rect.width-this.frameSize},${this.frameSize/2} ${this.rect.width-this.frameSize},${this.rect.height-this.frameSize/2} ${this.frameSize},${this.rect.height-this.frameSize} Z`,
                    prev: `M 0,0 0,${this.rect.height} ${this.rect.width},${this.rect.height} ${this.rect.width},0 0,0 Z M ${this.frameSize},${this.frameSize/2} ${this.rect.width-this.frameSize},${this.frameSize} ${this.rect.width-this.frameSize},${this.rect.height-this.frameSize} ${this.frameSize},${this.rect.height-this.frameSize/2} Z`
                }
            }
        }
        initEvents() {
            this.DOM.nextCtrl.addEventListener('click', () => this.navigate('next'));
            this.DOM.prevCtrl.addEventListener('click', () => this.navigate('prev'));
            
            window.addEventListener('resize', debounce(() => {
                this.rect = this.DOM.el.getBoundingClientRect();
                this.updateFrame();
            }, 20));
            
            document.addEventListener('keydown', (ev) => {
                const keyCode = ev.keyCode || ev.which;
                if ( keyCode === 37 ) {
                    this.navigate('prev');
                }
                else if ( keyCode === 39 ) {
                    this.navigate('next');
                }
            });
        }
        navigate(dir = 'next') {
            if ( this.isAnimating ) return false;
            this.isAnimating = true;

            const animateShapeIn = anime({
                targets: this.DOM.shape,
                duration: this.settings.animation.shape.duration,
                easing: this.settings.animation.shape.easing.in,
                d: dir === 'next' ? this.paths.final.next : this.paths.final.prev
            });

            const animateSlides = () => {
                return new Promise((resolve, reject) => {
                    const currentSlide = this.DOM.slides[this.current];
                    anime({
                        targets: currentSlide,
                        duration: this.settings.animation.slides.duration,
                        easing: this.settings.animation.slides.easing,
                        translateX: dir === 'next' ? -1*this.rect.width : this.rect.width,
                        complete: () => {
                            currentSlide.classList.remove('slide--current');
                            resolve();
                        }
                    });
        
                    this.current = dir === 'next' ?
                        this.current < this.slidesTotal-1 ? this.current + 1 : 0 :
                        this.current > 0 ? this.current - 1 : this.slidesTotal-1;
                    
                    const newSlide = this.DOM.slides[this.current];
                    newSlide.classList.add('slide--current');
                    anime({
                        targets: newSlide,
                        duration: this.settings.animation.slides.duration,
                        easing: this.settings.animation.slides.easing,
                        translateX: [dir === 'next' ? this.rect.width : -1*this.rect.width,0]
                    });
        
                    const newSlideImg = newSlide.querySelector('.slide__img');
                    newSlideImg.style.transformOrigin = dir === 'next' ? '-10% 50%' : '110% 50%';
                    anime.remove(newSlideImg);
                    anime({
                        targets: newSlideImg,
                        duration: this.settings.animation.slides.duration*4,
                        easing: 'easeOutElastic',
                        elasticity: 350,
                        scale: [1.2,1],
                        rotate: [dir === 'next' ? 4 : -4,0]
                    });
        
                    anime({
                        targets: [newSlide.querySelector('.slide__title'), newSlide.querySelector('.slide__desc'), newSlide.querySelector('.slide__link')],
                        duration: this.settings.animation.slides.duration,
                        easing: this.settings.animation.slides.easing,
                        delay: (t,i,total) => dir === 'next' ? i*100+750 : (total-i-1)*100+750,
                        translateY: [dir === 'next' ? 300 : -300,0],
                        rotate: [15,0],
                        opacity: [0,1]
                    });
                });
            };

            const animateShapeOut = () => {
                anime({
                    targets: this.DOM.shape,
                    duration: this.settings.animation.shape.duration,
                    delay: 150,
                    easing: this.settings.animation.shape.easing.out,
                    d: this.paths.initial,
                    complete: () => this.isAnimating = false
                });
            }

            animateShapeIn.finished.then(animateSlides).then(animateShapeOut);
        }
    };

    new Slideshow(document.querySelector('.slideshow'));
    imagesLoaded('.slide__img', { background: true }, () => document.body.classList.remove('loading'));

HTML:

Code:
<div class="slideshow">
    <div class="slides">
        <div class="slide slide--current" id="one">
            <div class="slide__img" style="background-image: url(img/start.jpg);"></div>
            <h2 class="slide__title">Jubiläum</h2>
            <p class="slide__desc">Created by Wortwerkstatt</p>
            <a class="slide__link menubutton1" href="#">Einführung</a>
        </div>
        <div class="slide" id="two">
            <div class="slide__img" style="background-image: url(img/chronik.jpg);"></div>
            <h2 class="slide__title">Chronik</h2>
            <p class="slide__desc">Digital und Print - die Geschichte schafft Ein- und Ausblicke, <br />verbindet historische Leistungen mit der Zukunft</p>
            <a class="slide__link menubutton3" href="#">Discover calmness</a>
        </div>
        <div class="slide" id="three">
            <div class="slide__img" style="background-image: url(img/presse-1.jpg);"></div>
            <h2 class="slide__title">Presse</h2>
            <p class="slide__desc">Kommunikation intern - und extern</p>
            <a class="slide__link" href="#">mehr dazu</a>
        </div>
        <div class="slide" id="four">
            <div class="slide__img" style="background-image: url(img/festakt.jpg);"></div>
            <h2 class="slide__title">Festakt</h2>
            <p class="slide__desc">The Hightlight. Unique. Full of moments to share and to remember.</p>
            <a class="slide__link" href="#">Adventure yourself</a>
        </div>
        <div class="slide" id="five">
            <div class="slide__img" style="background-image: url(img/familie.jpg);"></div>
            <h2 class="slide__title">Familientag</h2>
            <p class="slide__desc">Leave society behind and indulge yourself in tranquility ;-).</p>
            <a class="slide__link" href="#">Discover calmness</a>
        </div>
        <div class="slide" id="six">
            <div class="slide__img" style="background-image: url(img/museum-2.jpg); background-position:center 25%;"></div>
            <h2 class="slide__title">Exhibition</h2>
            <p class="slide__desc">Discover great activities with breathtaking views.</p>
            <a class="slide__link" href="#">Find out more</a>
        </div>
    </div>
                
    <!-- Thumb Navigation, doesn't work -->
    <nav class="thumbnav">
        <a href="#one"><img src="img/thumb1.jpg" alt="" /></a>
        <a href="#two"><img src="img/thumb2.jpg" alt="" /></a>
        <a href="#three"><img src="img/thumb3.jpg" alt="" /></a>
        <a href="#four"><img src="img/thumb4.jpg" alt="" /></a>
        <a href="#five"><img src="img/thumb5.jpg" alt="" /></a>
        <a href="#six"><img src="img/thumb6.jpg" alt="" /></a>
    </nav>
                
    <!-- This works: -->
    <nav class="slidenav">
        <button class="slidenav__item slidenav__item--prev">Previous</button>
        <span>/</span>
        <button class="slidenav__item slidenav__item--next">Next</button>
    </nav>
</div>
 

Ingresso

Active Member
#3
Wenn ich dir einen Tipp geben darf.
Anstatt sich von verschiedenen Seiten einzelne Snippets zu kopieren, vielleicht einfach ein Framework nehmen, welches die meisten Komponenten bereits beinhaltet.

Für deinen Anwendungsfall: https://getbootstrap.com/docs/4.3/components/carousel/#with-indicators
Oder spricht etwas dagegen?


Und noch ein kleiner Tipp:
Wenn du so langen Quellcode hast, dann stell doch bitte eine lauffähige Version deines Codes auf z.B. https://codepen.io/pen/ bereit, sodass man sich live ansehen kann, wie dein aktueller Stand ist.
Da bekomme ich direkt mehr Lust einfach mal nachzuschauen anstatt sich hier durch den nicht gehighlighten Code zu lesen.
 
#5
Wenn ich dir einen Tipp geben darf.
Anstatt sich von verschiedenen Seiten einzelne Snippets zu kopieren, vielleicht einfach ein Framework nehmen, welches die meisten Komponenten bereits beinhaltet.

Für deinen Anwendungsfall: https://getbootstrap.com/docs/4.3/components/carousel/#with-indicators
Oder spricht etwas dagegen?


Und noch ein kleiner Tipp:
Wenn du so langen Quellcode hast, dann stell doch bitte eine lauffähige Version deines Codes auf z.B. https://codepen.io/pen/ bereit, sodass man sich live ansehen kann, wie dein aktueller Stand ist.
Da bekomme ich direkt mehr Lust einfach mal nachzuschauen anstatt sich hier durch den nicht gehighlighten Code zu lesen.
Danke! Deine Hinweise erklären mir als Neuling, wie ich hier präziser rangehen sollte, werde gleich etwas enger gefasst und mit Beispiel zum anschauen nochmal das Problem posten...!
 
#6
Hallo nochmal,

bin bei der Thumbnail-Navigation für den Slider weiter gekommen. Das Problem ist nur noch, dass die Navigation in neueren Browsern funktioniert, in etwas älteren aber nicht.

Urspünglicher Zustand, funktioniert auch in älteren Browsern:
https://gerritsen-design.de/one/

Mit Autoplay und Thumbnail-Navigation, funktioniert nur in neueren Browsern:
https://gerritsen-design.de/two/

Das Problem, bei dem ich nun nicht weiter komme ist (meiner Vermutung nach) im slider.js, Zeile 133:
thumbItems.forEach((item, index) => {

Fehlermeldung:
TypeError: thumbItems.forEach is not a function

Hat jemand eine Ahnung, woran es liegen könnte?
 
#8
Das Problem ist, dass document.querySelectorAll() ein NodeList-Objekt zurückgibt.
Die Funktion forEach() kann in älteren Browsern allerdings nicht auf eine NodeList angewendet werden.

Mögliche Alternativen findest du hier:
https://css-tricks.com/snippets/javascript/loop-queryselectorall-matches/

Perfekt, vielen Dank!

querySelectorAll
zu:
Array.prototype.slice.call(document.querySelectorAll

war die Lösung, nun ist das Script auch für ältere Browser kompatibel!
 
Oben