• 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

+2 głosów
535 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ź 1,059 wizyt
pytanie zadane 16 lutego 2018 w JavaScript przez Paweł Piech Użytkownik (720 p.)
0 głosów
0 odpowiedzi 180 wizyt
pytanie zadane 12 lipca 2019 w JavaScript przez S-Type Nowicjusz (120 p.)
0 głosów
1 odpowiedź 594 wizyt
pytanie zadane 27 grudnia 2015 w JavaScript przez ScriptyChris Mędrzec (190,190 p.)

93,425 zapytań

142,421 odpowiedzi

322,647 komentarzy

62,787 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

VMware Cloud PRO - przenieś swoją infrastrukturę IT do chmury
...