• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Porównanie pary klas css w js

Object Storage Arubacloud
+2 głosów
365 wizyt
pytanie zadane 29 maja 2017 w JavaScript przez mowmiheniek Stary wyjadacz (11,900 p.)

Witam po dłuższej przerwie,

spędziłem już trochę czasu nad moim problemem, więc zwracam się do was o pomoc.

Mam dwie metody. Jedna pobiera wszystkie obiekty z klasą "no-clicked", po kliknięciu na obiekt z tą klasą dodaje do array. Po drugim kliknięciu na następny obiekt, metoda guessPair porównuje zawartość klikniętych obiektów.

Gdy są takie same, usuwa klasy "no-clicked" dodaje "clicked". Gdy nie są takie same, klasy zostawia bez zmian. Zabawa zaczyna się od początku z pozostałymi obiektami.

Nic skomplikowanego, a jednak.

Wszystko działa do pierwszej zgadniętej pary. Później wolna amerykanka i błędy: Uncaught TypeError: Cannot read property 'classList' of undefined.

    parseClick: function() {
        var characters = document.getElementsByClassName("no-clicked"),
            arrPair = new Array(0),
            arrCharacterLenght = characters.length,
            clickSound = document.getElementById("soundClick"),
            j = 0;
        if (arrCharacterLenght === 0) {
            setTimeout(view.congratulations, 2100);
        } else {

          function clickCheck(i) {
            characters[i].addEventListener("click", function() {
                var clickedCharacter = characters[i];
                console.log("klikniety "+clickedCharacter+" numer "+i);
                clickedCharacter.classList.add("clicked");
                j++;
                view.showUp(clickedCharacter);
                view.soundClick(clickSound);
                arrPair.push(clickedCharacter);
                if (j === 2) {
                    contnroller.guessPair(arrPair);
                    j = 0;
                    arrPair.length = 0;
                }
            }, false)
          };

        for (var i = 0; i < arrCharacterLenght; i++) {
          clickCheck(i);
        }
    }
  },
    guessPair: function(pair) {
        var firstLetter = pair[0].getElementsByTagName("p")[0].innerHTML,
            secondLetter = pair[1].getElementsByTagName("p")[0].innerHTML;
        if (firstLetter === secondLetter) {
            pair[0].classList.remove("no-clicked");
            pair[1].classList.remove("no-clicked");
            view.hit();
            contnroller.parseClick();
        } else {
            pair[0].classList.remove("clicked");
            pair[1].classList.remove("clicked");
            view.backHide(pair);
            view.miss();
        }
    },
}

Dzięki za pomoc.

 

komentarz 29 maja 2017 przez mowmiheniek Stary wyjadacz (11,900 p.)

Dadam jeszcze, że można przetestować tutaj:

https://guydiamond.github.io/where_is_my_letter/index.html

komentarz 30 maja 2017 przez ScriptyChris Mędrzec (190,190 p.)

Jeśli undefined dotyczy 15 linijki, to owiń 29 linijkę w IIFE albo zamień var w 28 linijce na let.

komentarz 30 maja 2017 przez mowmiheniek Stary wyjadacz (11,900 p.)
Niestety to nie rozwiązuje problemu. Mógłbyś zagrać w to z trzy razy z włączoną konsolą. Te błędy wyglądają na losowe. Nie ma reguły. Czasami można przejść grę bez błędu. A czasami wyskakują po pierwszej odgadniętej parze. Zdarza się też, że po jednym kliku wychodzą dwie naraz. Dzięki

1 odpowiedź

+3 głosów
odpowiedź 30 maja 2017 przez ScriptyChris Mędrzec (190,190 p.)
wybrane 30 maja 2017 przez mowmiheniek
 
Najlepsza

Najpierw pętlą lecisz po wszystkich elementach .no-clicked pobranych do zmiennej characters (jest ich, jeśli dobrze spojrzałem 5):

for (var i = 0; i < arrCharacterLenght; i++) {

, wewnątrz niej podpinasz do każdego EventListener (lepiej skorzystać w takim przypadku z EventDelegation) i w nim pobierasz sobie konkretnie klikniętego:

var clickedCharacter = characters[i];

Problem polega na tym, że z wybranego (klikniętego) elementu usuwasz tą klasę, ale pod zmienną arrCharacterLength nadal masz 5 elementów i jeśli klikniesz w któregoś, to w żywej kolekcji  (getElementsBy* taką zwraca) characters danego elementu już nie ma, stąd undefined.

P.S. Polecam odpalić debugger (zakładka Sources w Chrome) i postawić break point w 72 linijce pliku engineControllers.js. Graj i dopóki nie wystąpi błąd to pomijaj debugging (wznawiaj wykonanie skryptu klawiszem F8), a jeśli przy kliknięciu wystąpi błąd z undefined, to sobie podejrzyj w debuggerze, że próbujesz dostać się do elementu DOM, którego nie ma, bo np. zostały Ci 3-4 elementy z klasą "no-clicked", a chcesz się dostać do ostatniego.

komentarz 30 maja 2017 przez mowmiheniek Stary wyjadacz (11,900 p.)

Złoty chłopak z Ciebie :) Czuję, że przez Ciebie zaczynam pisać lepszy kod. smiley

Zwykle sprawdzam kod przez console.log. Przepisałem kod na EventDelegation, zmieniłem getElementsBy na querySelectorAll i błąd nie wyglądał tak losowo wink

Teraz nie było błędu z undefined, ale zawsze przedostatnia postać chowała się z trzecią (już odgadniętą).

Problemem był powrót do contnroller.parseClick(); w metodzie guessPair. Tak jakby nie zerowało j, nawet z let (niestety, nie wiem dla czego). Ale usunięcie tego powrotu, powodował, że gra się nie kończyła. Nie był sprawdzany warunek arrCharacterLenght === 0.

Umieściłem sprawdzenie w metodzie guessPair i działa!

Dziękuje bardzo, męczyłem się z tym długo.

Nowy kod:

    parseClick: function() {
        var characters = document.querySelectorAll(".no-clicked"),
            arrPair = new Array(0),
            clickSound = document.getElementById("soundClick"),
            j = 0;
            document.getElementById("monsters").addEventListener("click", function(e) {
              if (e.target && e.target.matches("div.no-clicked")) {
                var clickedCharacter = e.target;
                clickedCharacter.classList.add("clicked");
                j++;
                view.showUp(clickedCharacter);
                view.soundClick(clickSound);
                arrPair.push(clickedCharacter);
                if (j === 2) {
                    contnroller.guessPair(arrPair);
                    j = 0;
                    arrPair.length = 0;
                    }
                  }
                }, false)
  },
    guessPair: function(pair) {
        var firstLetter = pair[0].getElementsByTagName("p")[0].innerHTML,
            secondLetter = pair[1].getElementsByTagName("p")[0].innerHTML;
            console.log("1 litera="+firstLetter+" 2 litera="+secondLetter);
        if (firstLetter === secondLetter) {
            pair[0].classList.remove("no-clicked");
            pair[1].classList.remove("no-clicked");
            view.hit();
            let characters = document.querySelectorAll(".no-clicked");
            if (characters.length === 0) {
                setTimeout(view.congratulations, 2100);
           };
        } else {
            pair[0].classList.remove("clicked");
            pair[1].classList.remove("clicked");
            view.backHide(pair);
            view.miss();
        }
    },
}

Dobrze zastosowałem EventDelegation?

komentarz 31 maja 2017 przez mowmiheniek Stary wyjadacz (11,900 p.)

Jeszcze mam jedno pytanie, jeśli pozwolisz.

Dlaczego e.target.matches("div.no-clicked") działa na object a nie do diva z tą klasą?

<div class="picture no-clicked">
        <div class="speakBubbels">
          <p class="capital letter"></p>
          <audio class="soundLetter"  src="">
        </audio>
        </div>
        <object class="character" type="image/svg+xml" data=""></object>
</div>

 

1
komentarz 31 maja 2017 przez ScriptyChris Mędrzec (190,190 p.)

Dobrze zastosowałem EventDelegation?

W porządku.

 Dlaczego e.target.matches("div.no-clicked") działa na object a nie do diva z tą klasą?

Nie rozumiem.

komentarz 31 maja 2017 przez mowmiheniek Stary wyjadacz (11,900 p.)
Wyżej wkleiłem kod HTML.

W divie o klasie picture no-clicked jest div speakBubbels oraz object. Klikalny jest tylko object.
1
komentarz 31 maja 2017 przez ScriptyChris Mędrzec (190,190 p.)

Ja bym powiedział, że klikalne są:

<p class="capital letter">b</p>

oraz

<div class="picture no-clicked"></div>

Wykonsoluj sobie e.target i zobacz w co konkretnie klikasz.

2
komentarz 31 maja 2017 przez ScriptyChris Mędrzec (190,190 p.)

Jeśli chcesz sobie sprawdzić, czy to w co kliknąłeś znajduje się w div.no-clicked, który z kolei znajduje się w parentcie z podpiętym listenerem, to taki skrypcik powinien Ci się przydać:

function getClickableElement(event) {
	var parent = prevParent = event.target.parentNode;

    /** 
      * sprawdzaj, czy aktualny PARENT jest elementem DOM, 
      * pod który podpiales listener - jesli jest to przerwij pętle
      */
	while (parent !== event.currentTarget) {
		prevParent = parent.cloneNode(true);
		parent = parent.parentNode;
       }

    /**
      * zwraca => div.no-clicked, 
      * a jego bezposrednim parentem jest element DOM z podpiętym listenerem
      */
	return prevParent;
}

document.getElementById("monsters").addEventListener("click", function(e) {	
	var isWithinClickableElement = getClickableElement(e);

	if (e.target && isWithinClickableElement) { 
		/** reszta kodu */ 
	}
});

Jeśli nie rozumiesz czegoś, to pytaj :)

komentarz 31 maja 2017 przez mowmiheniek Stary wyjadacz (11,900 p.)

Sprawdziłem i wynik mnie zaskoczył.

Jednak klikalny jest object. Jest on pod id=monster. Reszta podpięta jest pod 

<div class="picture no-clicked"></div>

Zmodyfikuję kod tak, aby obydwa działały i będzie dobrze.

Dzięki!

Podobne pytania

0 głosów
1 odpowiedź 978 wizyt
pytanie zadane 16 lutego 2018 w JavaScript przez Paweł Piech Użytkownik (720 p.)
0 głosów
0 odpowiedzi 160 wizyt
pytanie zadane 12 lipca 2019 w JavaScript przez S-Type Nowicjusz (120 p.)
0 głosów
1 odpowiedź 529 wizyt
pytanie zadane 27 grudnia 2015 w JavaScript przez ScriptyChris Mędrzec (190,190 p.)

92,577 zapytań

141,426 odpowiedzi

319,652 komentarzy

61,961 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...