Images czyli grafika na stronie

Aby manipulować grafiką na stronie, musimy się do niej odwołać korzystając zmetod pobierających obiekty.


<img src="piesek.jpg" width="100" height="100" alt="piesek" />
<img src="kotek.jpg" width="100" height="100" alt="kotek" />
<img src="chomik.jpg" width="100" height="100" alt="chomik" />
<img src="rybka.jpg" width="100" height="100" alt="rybka" id="rybcia" />
<img src="chupacabra.jpg" width="100" height="100" alt="chupacabra" id="chupacabra" />

var images = document.images //korzystając z kolekcji
var images = document.querySelectorAll('img'); //nowa metoda
var fish = document.getElementById('rybcia');
var chupacabra = rybka.nextSibling();

Każdej grafice możemy ustawiać standardowe atrybuty html dla grafiki (border, name, height, width, hspace, vspace, lowsrc, src), lub też zmieniać jego style.


var obr = document.querySelectorAll('img')[2]; //3 grafika na stronie
console.log('Szerokosc: ' + img.width + ', wysokosc: ' + img.height + ', src:' + img.src);

Efekt rollover

Do efektu rollover powinien być stosowany zwykły CSS. Jeżeli nie masz powodu by wykorzystywać do tego JS - nie rób tego. Koniec kropka.

Dzięki Javascript nie musimy się ograniczać tylko do sytuacji, gdy obrazek wskazujemy kursorem. Podmiana obrazka może nastąpić podczas dowolnego zdarzenia, jakie obsługuje Javascript.

Aby podmienić grafikę na inną musimy zmienić atrybut src danej grafiki:


var obr = document.getElementById('obrazek');

img.addEventListener('mouseover', function() {
    this.src = 'obrazek_2.jpg';
});

img.addEventListener('mouseout', function() {
    this.src = 'obrazek_1.jpg';
});

Efekt rollower z wcześniejszym załadowaniem obrazków

Powyższa metoda ma poważny błąd. Nowa grafika zostaje ściągana dopiero, gdy wskażemy nasz obrazek kursorem. Przy wolnych łączach lub dużych grafikach spowoduje to brak płynności.
Aby temu zapobiec musimy wszystkie grafiki biorące udział w efekcie rollover załadować do cache przeglądarki (czyli je wcześniej pobrać). Wówczas, w chwili wskazania obrazka, skrypt szybko podmieni jeden obrazek na drugi.

Aby załadować grafiki do cache musimy skorzystać z obiektu typu Image, któremu ustawimy odpowiednią właściwość src. Podstawowa deklaracja tego typu obiektu ma postać:


if (document.images) { //sprawdzamy czy przeglądarka obsługuje obiekty images
    var nowyImage = new Image(width, height) //tworzymy nowy obiekt Image - parametry width i height są opcjonalne
        nowyImage.src = "obrazek_on.jpg" //podajemy jego src
}

Gdy utworzymy już stosowne obiekty, możemy odwoływać się do ich właściwości src. Poniższy skrypt realizuje całe zadanie:


if (document.images) {
    var imageOff = new Image();
    imageOff.src = '/images/obrazek1.jpg';

    var imageOn = new Image();
    imageOn.src = '/images/obrazek2.jpg';

    var img = document.getElementById('obrazek');

    img.addEventListener('mouseover', function() {
        this.src = imageOn.src;
    });
    img.addEventListener('mouseout', function() {
        this.src = imageOff.src;
    });
}

Oczywiście nie tylko zdarzeniem mouseover człowiek żyje. Można przecież także i click wykorzystać:

Kliknij!
Zastrzel kartofla!

Powyższy efekt kliknięcia także można uzyskać z użyciem tylko CSS. Wystarczy wykorzystać do tego selektor :checked i selektor rodzeństwa ~. Poniżej zamieszczam skrócony wzór:


    <label class="cnt">
        <input type="checkbox">
        <div class="off"></div>
        <div class="on"></div>
    </label>

    .cnt {display:block; overflow:hidden;}
    .cnt input {left:-99999px; position:absolute;}
    .cnt .off {display:block;}
    .cnt .on {display:none;}
    .cnt input:checked ~ .off {display:none;}
    .cnt input:checked ~ .on {display:block;}

Efekt rollower dla większej ilości obrazków

Deklarowanie dla każdej grafiki obu stanów w podany powyżej sposób może być problematyczne - zwłasza dla większej liczby grafik. O wiele lepszym rozwiązaniem jest zastosowanie tablicy do przechowywania nazw obrazków, a następnie za pomocą pętli dynamiczne tworzenie obiektów Image, które będziemy trzymać w dodatkowej tablicy:


var names = [
    'obrazek.jpg',
    'kartofelek.jpg',
    'piesek.jpg',
    'kotek.jpg',
    'czekolada.jpg'
];
var images = [];

for (var x=0; x<names.length; x++) {
    var images[x] = new Image();
    images[x].src = names[x];
}

Pasek wczytywania

Obiekty takie jak image czy window posiadają zdarzenie load, które wykrywa, czy dany obiekt został w pełni załadowany.

Dla obiektu window zdarzenie to oznacza wczytanie całego dokument dom oraz wszystkich grafik (w przeciwieństwie do DOMContentLoaded, które odpalane jest po wczytaniu drzewa DOM).
Dla grafiki oznacza to wczytanie grafiki:


img.addEventListener('load', function() {
    console.log('Dana grafika została załadowana');
});
img.src = 'lorem.jpg'; //wpierw ustawiamy zdarzenie, potem ustawiamy src

Proste prawda? Nie do końca. Okazuje się, że jeżeli dany element ma już wcześniej ustawione src (np jest zdefiniowany w dokumencie html), a obrazek został wcześniej już wczytany i trafił do cache przeglądarki, zdarzenie to nie zostanie odpalone. Na szczęście js udostępnia właściwość complete, która mówi nam, czy dana grafika została załadowana:


var img = document.getElementById('someImageOnPage');
    img.addEventListener('load', function() {
    console.log('Dana grafika została załadowana');
});
if (img.complete) {
    img.dispatchEvent('load');
}

Pasek wczytywania

Nazwy obrazków do załadowania podajemy w formie tablicy. Następnie wykonujemy pętlę po tej tablicy, tworząc obiekty Image z odpowiednim src. Dla każdego obiektu definiujemy zdarzenie onload. Będzie ono wywoływało funkcję, która sprawdza ile obiektów zostało już załadowanych i odpowiednio ustawiało długość paska ładowania (w procentach). Aby móc sprawdzać ile obiektów zostało załadowanych, musimy posłużyć się dodatkową zmienną ile_zaladowano.


<style>
    .loading-bg {
        margin:30px 0;
        width:600px;
        height:40px;
        background:#eee;
        border:1px solid #ddd;
        overflow:hidden;
    }
    .loading-bg .progress {
        width:0;
        height:100%;
        background:#EC185D;
        overflow:hidden
    }
</style>
<div id="progressCnt"></div>

(function() {
    //tablica z nazwami obrazków do załadowania
    var imgNames = [
        'obrazek1.gif',
        'obrazek2.gif',
        'obrazek3.gif',
        'obrazek4.gif',
        'obrazek5.gif',
        'obrazek6.gif'
    ];
    var howLoaded = 0; //ile obiektów Images już załadowano do pamięci
    var loadingStep = (100 / imgNames.length); //szerokość oznaczająca % paska po załadowaniu 1 obrazka
    var images = []; //tablica będzie zawierała obiekty Image
    var loadingBarBg = null; //zmienna pod którą utworzymy dynamicznie div zawierającego div-pasek postępu
    var loadingBar = null; //zmienna pod którą utworzymy dynamicznie div-pasek postępu
    var pageToRedirect = 'index.php'; //strona na którą przeniesie po zakończonym ładowaniu

    //funkcja odpalana dla każdego obiektu Image, które wcześniej stworzyliśmy.
    //Sprawdza ile obiektów zostało załadowanych i ustawia odpowiednią szerokość paska.
    function setLoadingBar() {
        howLoaded++;
        loadingBar.style.width = howLoaded * loadingStep + "%"; //zmianiamy szerokość paska (podaną w %)

        if (howLoaded >= imgNames.length) {
            setTimeout(function() {
                location.href = pageToRedirect; //po załadowaniu wszystkich grafik czekamy 2s i przenosimy na stronę
            }, 2000)
        }
    }

    //funkcja rozpoczynająca ładowanie obrazków
    function startLoading() {
        var div = document.querySelector('#progressCnt');

        loadingBarBg = document.createElement('div');
        loadingBarBg.className = 'loading-bg';    //dzięki temu skorzystamy ze zdefiniowanych styli

        loadingBar = document.createElement('div');
        loadingBar.className = 'progress';

        loadingBarBg.appendChild(loadingBar);

        div.appendChild(loadingBarBg);

        for (var x=0; x<imgNames.length; x++) { //pętla po nazwach obrazków...
            images[x] = new Image();
            images[x].onload = setLoadingBar;    //dla każdego obiektu ustawiamy zdarzenie onload
            images[x].src = imgNames[x];
        }
    }

    document.addEventListener("DOMContentLoaded", function(event) {
        startLoading();
    });
});

Powyższy przykład możesz zobaczyć w działaniu tutaj.


Trening czyni mistrza

Poniżej zamieszczam kilka zadań, które w ramach ćwiczenia możesz wykonać:

  1. Na środku powyżej stworzonego postępu ładowania dodaj tekst, który będzie pokazywał liczbę wczytywanych obrazków w formacie "Wczytano 5 / 10"