galeria z oknem modalnym, wersja 2

0

Jakiś czas temu popełniłem podobny projekt. Na podstawie Waszych uwag stwierdziłem, że:

  • kod był rakotwórczy
  • kod był "uszyty" pod konkretny zestaw znaczników HTML, czyli nieuniwersalny

Mając już troszkę więcej doświadczenia i wyczucia napisałem ten kod jeszcze raz. Póki co prezentuję wersję funkcyjną, może niebawem zrobię wersję OOP. W każdym razie tak starałem się pisać kod, by był uniwersalny i pozwalał dodać coś w przyszłości. Czyli teraz nie ma znaczenia, ile zdjęć jest na stronie. Dodanie nowej galerii również nie wymaga ingerencji w kod. Za każdym razem skrypt działa tak samo. Jedyne miejsce gdzie coś ewentualnie będzie wymagało zmiany, to nazwy class i id znaczników HTML, które chwyciłem na początku kodu jako const.

Nazwy znaczników HTML pomińcie, celowo użyłem tych z poprzedniej wersji.

Kod HTML:

<body>   
            <p>Sypialnia</p>
            <div class = "galeria">
                <img class = "mini" src = sypialnia1.jpg>
                <img class = "mini" src = sypialnia2.jpg>
                <img class = "mini" src = sypialnia3.jpg>
            </div>
             <p>Kuchnia</p>
            <div class = "galeria">
                <img class = "mini" src = kuchnia1.jpg>
                <img class = "mini" src = kuchnia2.jpg>
                <img class = "mini" src = kuchnia3.jpg>
            </div>
            
            <div id = "modal_okno">
                <span id = "modal_zamknij">&times;</span>
                <span id = "modal_next">&#10095;</span>
                <img id = "modal_obraz" src = (1).jpg>  
                <span id = "modal_prev">&#10094;</span>
                <span id = "modal_numer"></span>
            </div>
</body>

Kod JS:

const pictureClassName = "mini";
const gallery = document.getElementsByClassName("galeria");
const windowModal = document.getElementById("modal_okno");
const pictureModal = document.getElementById("modal_obraz");
const buttonClose = document.getElementById("modal_zamknij");
const buttonNext = document.getElementById("modal_next");
const buttonPrev = document.getElementById("modal_prev");
const pictureId = document.getElementById("modal_numer");

buttonClose.addEventListener('click', hideModal);
buttonNext.addEventListener('click', (e) => nextPicture(e));
buttonPrev.addEventListener('click', (e) => previousPicture(e));

for (let i = 0; i < gallery.length; i++)
{
    gallery[i].addEventListener('click', (e) =>
    {
        if (e.target.className == pictureClassName)
        {
            let pictureName = getName(e);
            showModal();
            putPicture(pictureName);
            showPictureNumber(pictureName);
        }
    });
}

function getName(e)
{
    let name = e.target.getAttribute("src");
    return name;
}

function showModal()
{
    windowModal.style.display = "block";
}

function hideModal()
{
    windowModal.style.display = "none";
}

function putPicture(pictureName)
{
    pictureModal.src = pictureName;
}

function showPictureNumber(pictureName)
{
    let pictures = document.querySelectorAll('img[class~=' + pictureClassName + ']');
    let pictureNumberTotal = pictures.length;
    let pictureNumberShown = 0;
    pictures.forEach((el, index) => 
        {
            if (el.getAttribute("src") == pictureName)
            {
                pictureNumberShown = index+1;
            }
        });
    let pictureLabel = pictureNumberShown + "/" + pictureNumberTotal;     
    pictureId.textContent = pictureLabel; 
}

function nextPicture(e)
{
    let currentPicture = e.target.nextElementSibling.getAttribute("src");
    putPicture(findNextPicture(currentPicture, "next"));
}

function previousPicture(e)
{
    let currentPicture = e.target.previousElementSibling.getAttribute("src");
    putPicture(findNextPicture(currentPicture, "prev"));
}

function findNextPicture(currentPicture, direction)
{
    let anotherPicture = "";
    let pictures = document.querySelectorAll('img[class~=' + pictureClassName + ']');
    pictures.forEach((el, index) => 
        {
            if (el.getAttribute("src") == currentPicture)
            {
                switch (direction) 
                {
                    case "next":
                        anotherPicture = pictures[Math.min(pictures.length-1, index+1)].getAttribute("src");
                        break;
                    case "prev":
                        anotherPicture = pictures[Math.max(0, index-1)].getAttribute("src");
                        break;
                }
                showPictureNumber(anotherPicture);
            }
        });
    return anotherPicture;
}

document.addEventListener('keydown', (e) =>
{
    if (windowModal.style.display == "block")
    {
        switch (e.keyCode) 
        {
            case 37:
                putPicture(findNextPicture(pictureModal.getAttribute("src"), "prev"));
                break;
            case 39:
                putPicture(findNextPicture(pictureModal.getAttribute("src"), "next"));
                break;
        }
    }
});

Zastanawiam się jeszcze, czy zamiast forEach nie lepiej użyć filter. Tylko wtedy dodatkowo jeszcze trzeba przerobić obiekt na tablicę. A może jest lepszy sposób na takie przefiltrowanie listy elementów img?

0
kosmonauta80 napisał(a):

Zastanawiam się jeszcze, czy zamiast forEach nie lepiej użyć filter. Tylko wtedy dodatkowo jeszcze trzeba przerobić obiekt na tablicę. A może jest lepszy sposób na takie przefiltrowanie listy elementów img?

Można to zrobić w prosty sposób za pomocą metody querySelector/querySelectorAll i selektora :scope.
Pozwala nam to dowolnie filtrować DOM tak jakbyśmy pisali zwykły CSS.

<div id="parent">
    <div>foo</div>
    <div>bar</div>
    <div class="child">
      <span>foobar</span>
    </div>
</div>
const parent = document.querySelector('#parent');
const spanContext = parent.querySelector(':scope .child span').textContext; // foobar

albo tak jak powiedziałeś za pomocą filter

const divs = document.querySelectorAll('#parent div');
const spanContext = [...divs].filter(element => element.classList.contains('child'))[0].textContent; // foobar
0

W linii 51 i 80 mam to samo, czyli:

 let pictures = document.querySelectorAll('img[class~=' + pictureClassName + ']');

A gdyby tak na początku zadeklarować to jako:

 const pictures = document.querySelectorAll('img[class~=' + pictureClassName + ']');

Dzięki temu przy wołaniu danej funkcji ma ona już gotowy zestaw (obiekt). Tylko z drugiej strony takie coś załaduje się do pamięci przeglądarki od razu. Co jeżeli obrazków będzie powiedzmy 100? Jest sens zapychać tak pamięć przeglądarki? Bo obecnie obrazki łapiemy tylko przy kliknięciu na przycisk Next/Prev.

2

W trakcie wdrażania mojego kodu zaszła konieczność jego drobnej zmiany. Po prostu w 2 miejscach musiałem zmodyfikować string. I wiecie co?
Bez problemu udało się to zrobić. Czemu? Bo skrypt został rozbity na wiele mini-funkcji. Tak jak mnie "uczyliście". Dzięki Panowie :D

1 użytkowników online, w tym zalogowanych: 0, gości: 1