קורס Front-End Web Development שיעור תרגיל סיכום - משחק תפוס תאדום


זה נושא דיון מלווה לערך המקורי ב- https://www.tocode.co.il/bundles/html5-web-development/lessons/catch-red-lab

. ```
.

Catch The red Assigment... .max { background-color:orange; width: 40px; } p { text-emphasis-position: }
    .game div {
        width:100px;
        height:100px;
        display:inline-block;
        background:blue;
        margin: 10px;  
    }

    .game {
            width:250px;
            margin:0 auto;
            outline: 1px solid;     
          }

    .game .winner {
        background:red;
    }

score:

var score=document.querySelector('.score'); score.textContent='score: '; var numScore=0; score.textContent+=numScore;
    function updateScore(event){
        if (event.target.className=='winner'){
            score.textContent='score: ';
            numScore+=5;
            score.textContent+=numScore;
        }else{
            score.textContent='score: '; 
            numScore-=5;
            score.textContent+=numScore;
        }        
    }//end of function

  
    function shuffle(event){
        

        for(var i =0;i<innerSquares.length;i++)
        {

            if(innerSquares[i].className='winner'){
                innerSquares[i].classList.remove('winner');
                innerSquares[i].classList.add('looser');
                
            }
                
        }
        var rnd = Math.floor(Math.random() *4 );
        if(innerSquares[rnd].className='looser'){
            innerSquares[rnd].classList.remove('looser');
            innerSquares[rnd].classList.add('winner');
        }
    }
  

    var boardGame = document.body.querySelector(".game");
    var innerSquares = boardGame.querySelectorAll("div");  
    boardGame.addEventListener('click',updateScore);
    boardGame.addEventListener('click',shuffle);
    
</script>
. ``` ברצוני לשתף את הפתרון שכתבתי למשימה תפוס תאדום הערות כלליות : ביצעתי הדבקה מלאה של הקוד בדף HTML אחד לשם נוחות בלבד הייתי שמח לראות פתרונות אחרים ,ובכל מקרה אשמח לענות על כל שאלה לגבי הקוד שכתבתי בתקווה לעוזר לאנשים אחרים להבין. התרגיל היה סיכום טוב לשיעור. אבל משום מה ינון שאלה שאני מפנה אלייך ,לא הצלחתי לכתוב שורת קוד שתמנע קריאה לפונקציות הטיפול במידה ואני לוחץ על אזורים שהם לא הריבועים במשחק. .אשמח גם שתתן קישור לפתרון שלך :slight_smile:

התרגיל המלא שוב כי שמתי לב שהוא הודבר לא טוב …

הפתרון שלי במלאו למשימה תפוס תאדום:

<!DOCTYPE html>
<html>
    <head>
        <title>Catch The red Assigment...</title>
    </head>
<style>
        .max {
            background-color:orange;
            width: 40px;
        }
        p {
            text-emphasis-position: 
        }

        .game div {
            width:100px;
            height:100px;
            display:inline-block;
            background:blue;
            margin: 10px;  
        }

        .game {
                width:250px;
                margin:0 auto;
                outline: 1px solid;     
              }

        .game .winner {
            background:red;
        }
</style>


<body>    
    <p class="score">score:</p>
    <div class="game">
        <div class="winner" ></div>
        <div class="looser"></div>
        <div class="looser"></div>
        <div class="looser"></div>    
    </div>
</body>
<script>        
        var score=document.querySelector('.score');
        score.textContent='score: ';
        var numScore=0;
        score.textContent+=numScore;
        
        function updateScore(event){
            if (event.target.className=='winner'){
                score.textContent='score: ';
                numScore+=5;
                score.textContent+=numScore;
            }else{
                score.textContent='score: '; 
                numScore-=5;
                score.textContent+=numScore;
            }        
        }//end of function
    
      
        function shuffle(event){
            
    
            for(var i =0;i<innerSquares.length;i++)
            {
    
                if(innerSquares[i].className='winner'){
                    innerSquares[i].classList.remove('winner');
                    innerSquares[i].classList.add('looser');
                    
                }
                    
            }
            var rnd = Math.floor(Math.random() *4 );
            if(innerSquares[rnd].className='looser'){
                innerSquares[rnd].classList.remove('looser');
                innerSquares[rnd].classList.add('winner');
            }
        }
      
    
        var boardGame = document.body.querySelector(".game");
        var innerSquares = boardGame.querySelectorAll("div");  
        boardGame.addEventListener('click',updateScore);
        boardGame.addEventListener('click',shuffle);
        
    </script>

    



</html>





הי קובי נראה טוב לשאלתך הבעיה בפונקציה updateScore: כמו שכתבת אותה אתה מניח שיש רק שתי אפשרויות או שיש קלאס winner או שלא ואז אתה מניח שיש קלאס looser, אבל חבר אמר לי פעם ש״חתול הוא לא ההפך מכלב״ ואותו הדבר נכון גם כאן.

תנאי if/else כותבים רק כשיש בדיוק שתי אפשרויות משלימות, אבל במקרה שלך יש אפשרות שלישית שלא התיחסת אליה והיא שמישהו לוחץ על אזור שהוא לא winner ולא looser. במצב כזה אתה רוצה פשוט להתעלם מהלחיצה.

נסה לשכתב את הפונקציה ולבדוק ספציפית אם לחצו על winner או על looser או על אף אחד משניהם

תודה יינון על הפידבק והכיוון וכתבתי מחדש את הפונקציה כך שלא תעדכן את הניקוד במקרה שלא לוחצים על אלמנט הריבוע…

<!DOCTYPE html>
<html>
    <head>
        <title>Catch The red Assigment...</title>
    </head>
<style>
        .max {
            background-color:orange;
            width: 40px;
        }
        p {
            text-emphasis-position: 
        }

        .game div {
            width:100px;
            height:100px;
            display:inline-block;
            background:blue;
            margin: 10px;  
        }

        .game {
                width:250px;
                margin:0 auto;
                outline: 1px solid;     
              }

        .game .winner {
            background:red;
        }
</style>


<body>    
    <p class="score">score:</p>
    <div class="game">
        <div class="winner" ></div>
        <div class="looser"></div>
        <div class="looser"></div>
        <div class="looser"></div>    
    </div>
</body>
<script>        
        var score=document.querySelector('.score');
        score.textContent='score: ';
        var numScore=0;
        score.textContent+=numScore;
        
        function updateScore(event){
            if (event.target.className=='winner'){
                score.textContent='score: ';
                numScore+=5;
                score.textContent+=numScore;
            }else if(event.target.className=='looser'){
                score.textContent='score: '; 
                numScore-=5;
                score.textContent+=numScore;
            }else {
                score.textContent='score: ';
                score.textContent+=numScore;
            }        
        }//end of function
    
      
        function shuffle(event){
            
    
            for(var i =0;i<innerSquares.length;i++)
            {
    
                if(innerSquares[i].className='winner'){
                    innerSquares[i].classList.remove('winner');
                    innerSquares[i].classList.add('looser');
                    
                }
                    
            }
            var rnd = Math.floor(Math.random() *4 );
            if(innerSquares[rnd].className='looser'){
                innerSquares[rnd].classList.remove('looser');
                innerSquares[rnd].classList.add('winner');
            }
        }
      
    
        var boardGame = document.body.querySelector(".game");
        var innerSquares = boardGame.querySelectorAll("div");  
        boardGame.addEventListener('click',updateScore);
        boardGame.addEventListener('click',shuffle);
        
    </script>





</html>

לייק 1

שינוי נוסף למשחק הוספתי תנאי בפונקציית shuffle שלא תבצע ערבוב אם לחצנו בשטח שאינו באלמנט הריבוע :

 
        function shuffle(event){
            
         if(event.target.className==='winner' || event.target.className==='looser'){
            for(var i =0;i<innerSquares.length;i++)
            {
    
                if(innerSquares[i].className='winner'){
                    innerSquares[i].classList.remove('winner');
                    innerSquares[i].classList.add('looser');
                    
                }
                    
            }
            var rnd = Math.floor(Math.random() *4 );
            if(innerSquares[rnd].className='looser'){
                innerSquares[rnd].classList.remove('looser');
                innerSquares[rnd].classList.add('winner');
            }
        }else{return;}
            
    }
לייק 1

זה הפתרון שלי - ע"מ שאוכל לגשת למספר של הנקודות הוספתי את המספר שבתוך הפסקה לתגית נפרדת

var game = document.querySelector(".game");
var divs = document.querySelectorAll(".game div");
var p =document.querySelector(".score span");

function refreshGame() {
    var win = document.querySelector(".game .winner");
    win.classList.remove("winner");
    
    var index = Math.floor(Math.random() * divs.length);
    divs[index].classList.add("winner");
}

function setScore(event) {
    var s = event.target;
    if (!s.parentElement===game){
       return;
   }
   var num = Number(p.textContent);
   if (s.classList.contains("winner")) {
       num+=5;
   }
    else{
        num -=5;
    }
    p.textContent=num;
    refreshGame();
}


game.addEventListener("click",setScore);
לייק 1

מעולה לסגור ערכים משתנים בתגית נפרדת זה טריק שאני אוהב והוא עוד ילווה אותך בהרבה מקומות בפיתוח Front End (וזה גם מה שקורה בפועל כשעובדים עם הרבה Frameworks למשל ריאקט מתנהג כך אוטומטית)

אגב עדכנתי לך קצת את ההודעה - סימן ה״שלוש פעמים גרש הפוך״ צריך להופיע מיד לפני תחילת הקוד, והטקסט צריך להופיע מחוץ לבלוק הזה כדי שלא יתערבב.

איך צורת הפתרון ? עובד לי מצויין רק רציתי לדעת איך הדרך


var counter = 0;
var p = document.querySelector('.score');
function eventBTN(event)
{
    debugger;
    var btn =event.target;
    if(btn.className == 'winner')
        counter= counter+5;
    else if (btn.className == '')
        counter= counter-5;
    p.textContent = counter;
}

var btn = document.querySelector('.game');
btn.addEventListener('click',eventBTN);

הי,

לא הבנתי - איפה הקוד שגורם לריבוע האדום לזוז למקום אחר אחרי כל לחיצה?

עוד כמה הערות קטנות על סגנון:

  1. ב JavaScript נהוג לשים את ה { באותה שורה כמו הפונקציה, כלומר לכתוב:
function eventBTN(event) {
}
  1. ב JavaScript נהוג לתת שמות לפונקציות ולמשתנים באותיות קטנות, ואם יש כמה מילים אז החל מהמילה השניה להשתמש באות ראשונה גדולה. במקרה שלך עדיף היה לקרוא לפונקציה eventBtn. אבל האמת שגם זה לא מושלם כי מומלץ להתחיל שם של פונקציה בפועל ושהשם ישקף את המטרה של הפונקציה. הייתי מעדיף שם כמו updateScore עם הקוד שלך.

  2. ממש מומלץ להשתמש תמיד ב { ו- } מסביב לבלוק של ה if גם אם זה בלוק של שורה אחת. את אף פעם לא יודעת מתי תוסיפי עוד שורות שם ולכן קל להתבלבל בהמשך.

  3. השימוש ב className לא מומלץ כדי לגלות אם לאלמנט יש קלאס (כמו winner), כי לאלמנט יכולים להיות מספר קלאסים מופרדים ברווחים. עדיף לשאול:

if (btn.classList.contains('winner') { ... }

כאן יש דוגמא קטנה לאלמנט עם מספר קלאסים:

הי ינון,

הנה הפיתרון שלי לתרגיל המשחק:

מה דעתך?

בנוגע לוולידציית לחיצה בין השוליים של אלמנטי ה-div-ים (כך שהלחיצה תחושב רק לשטחים הכחולים או האדום בלבד),
לא הצלחתי לקודד זאת (הוספתי בהערה ניסיון סימון של השוליים אבל ב-console לא הראה הבדל בין הערכים לאפשרויות ההקלקה).
איך בכל זאת ניתן ליישם וולידציה זו?
תודה.

לייק 1

הי,
כן אני רואה שניסית די הרבה אפשרויות.
הייתי מתחיל מלהוסיף קלאס לכל אחד מהריבועים שאתה צריך לזהות, נקרא לו box.

עכשיו תזכור שאתה מקבל כפרמטר את event ולו יש שדה בשם target שהוא האלמנט שלחצו עליו. תזכור גם שלכל אלמנט יש שדה בשם classList ולו פונקציה בשם contains. כשמחברים הכל ביחד מקבלים:

if (event.target.classList.contains('box')) { ... }

או קי, אז ניסיתי להגדיר אלמנט div box וזה קצת הסתבך בלוגיקה.
אבל כשחקרתי לפי inspect element גיליתי שבין ה-div-ים זה בעצם div.game
ובהמשך לכיוון שנתת לי כשהוספתי התניה לאלמנט האחרון זה הצליח.

לייק 1

אשמח אם תוכל לעבור על התרגיל

נראה כיף!

מה דעתך להוסיף שעון כך שאם שחקן לא לוחץ על כלום אחרי שתי שניות ירדו נקודות בצורה אוטומטית? ואולי הזמן מתקצר ככל שעולות הנקודות?

ועוד - מה דעתך להוסיף אפשרות ששחקן יוכל לבחור כמה ריבועים לקבל?

כמה מילים לגבי הקוד-

  1. השורה הזאת מוזרה:
    if(square.className == "game"){
        return;
    };

מה שאת עושה שם זה בודקת את כל ערך ה class של האלמנט. אבל אם לאלמנט יש class שמורכב מכמה מילים הקוד שלך לא יזהה את זה. בשביל לבדוק אם מאפיין class מכיל ערך מסוים משתמשים ב classList כלומר הייתי מצפה שיהיה כתוב שם:

if (square.classList.contains('game')) { ... }

דבר נוסף לא הבנתי למה לכתוב את זה:

parseInt("5");

ולא פשוט את המספר 5…

חוץ מאלה הקוד נראה אחלה - קצר, מדויק ועושה את העבודה. ממליץ לעשות את התיקונים וגם להוסיף את שני הפיצ׳רים שהצעתי למעלה

בהצלחה

יש המון שיטות להצליח במשימה הזאת, כל אחד יכתוב קוד אחר.
אני מאמין שיש מה לשפר אצלי, או לכתוב את זה בדרך יותר נכונה וביצועים יותר טובים.
הנה הקוד שלי לפתרון (כל שאר הקבצים נשארו כמו שינון הביא)

const boxes = document.querySelectorAll("div > div");
const score = document.querySelector(".score");
let scoreWin = 0;

for (box of boxes) {
  box.addEventListener("click", (e) => {
    if (e.target.classList.contains("winner")) {
      scoreWin += 5;
      score.textContent = `Score: ${scoreWin}`;
    } else {
      scoreWin -= 5;
      score.textContent = `Score: ${scoreWin}`;
    }
    boxes.forEach((box) => {
      if (box.classList.contains("winner")) {
        box.classList.remove("winner");
      }
    });
    let indexOfBox = Math.floor(Math.random() * boxes.length);
    boxes[indexOfBox].classList.add("winner");
  });
}
לייק 1

קצר ומדויק. מאוד אהבתי.

דבר קטן שהייתי משנה מבחינת קונספט זה להכניס Event Listener יחיד על המיכל הראשי במקום Event Listener לכל קופסא.

ועוד הצעה לגבי ביטול הקלאס winner - אין טעם לסרוק את כל הקופסאות ולבדוק אחת אחת אם היא מכילה את winner, יותר קל להשתמש ב querySelector נוסף ולהגיע ישר לקופסא עם הקלאס winner