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


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

היי יינון ,רציתי לדעת האם במחלקה GamePage שאני אמור ליצור להצגת דף המשחק
אני אצטרך להציג את האלמנט עם הקלאס “winner” והקלאס “looser” מהדום כשדה בתוך המחלקה שיצביע עלייה?

ולא כל כך הבנתי מדוע בקלאס ClickerPage ישנה בבנאי קריאה ל:

Page.prototype.enter.call(this, el);

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

הי,

נתחיל עם השאלה השניה - הקריאה:

Page.prototype.enter.call(this, el);

מפעילה את הפונקציה enter מתוך המחלקה Page. שים לב ש ClickerPage יורש מ Page וקריאה זו מופיעה בתוך המימוש של enter של ClickerPage - כלומר מימוש זה יצטרך לעשות כל מה ש enter של Page עושה ועוד קצת.

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

טוב אז זהו הפתרון שלי
את השינויים עשיתי בקובץ index.html:

<!DOCTYPE html>
<html>
  <head>
    <title>Single Page App Demo</title>
  </head>
  <style>
      .max {
          background-color:orange;
          width: 40px;
      }
      p {
          text-emphasis-position: inherit
      }

      .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>
    <main>
      Loading...
    </main>
    
    <script type="text/template" id="t-home">
      <h1>Home Page</h1>
      <p>
        This is the home page. 
        Go to <a href="#about">about</a> page
        Go to <a href="#game">game</a> Page
        Go to <a href="#clicker">clicker</a> page
      </p>
    </script>
    
    <script type="text/template" id="t-about">
      <h1>About </h1>
      <h4>
      <p>
          ברוכים הבאים למשחק ,"תפוס תאדום" המשחק הכי נחמד בעיר
          המשחק ביסודו פשוט מאוד ,ישנו לוח משחק המחולק לארבעה ריבועים
          שאחד אדום והשאר כחולים. מטרת המשחק היא לבצע לחיצה על שטח אחד הריבועים 
          כאשר לחיצה על הריבוע האדום תזכה את הניקוד שלכם ב5 נקד' ולחיצה על הריבוע
          הכחול תגרע ממכם 5 נק',כל העניין לבצע את הלחיצות כמה שיותר מהר כדי להביא לערבוב מהיר
          של הריבועים והנאה ממשוכת
      </p>
    </h4>

      <p>This is the about page. Go back to <a href="#">home</a> page</p>
    </script>
    
    <script type="text/template" id="t-clicker">
      <button>Click</button>
      <a href="#">Home</a>
    </script>

    <script type="text/template" id="t-game">
      <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>
      <p>This is the game page. Go back to <a href="#">home</a> page</p>
    </script>





    <script src="01-application.js"></script>
    <script src="02-page.js"></script>
    <script src="03-simplepages.js"></script>
    <script src="04-clickerpage.js"></script>
    <script src="05-main.js"></script>
    <script src="style.css"></script>
  </body>
</html>


והוספת Game Page class בקובץ simplepages.js:

//**********************************************************//
function GamePage(){
  Page.call(this,'t-game');
}

GamePage.prototype=Object.create(Page.prototype);

GamePage.prototype.enter = function(el){

  this._el=el; //this conatin the new Page html from game template


var score=this._el.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){
    
 if(event.target.className==='looser' || event.target.className==='winner'){
    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;}
    
}


var boardGame = this._el.querySelector(".game");
var innerSquares = boardGame.querySelectorAll("div");  
boardGame.addEventListener('click',updateScore);//this for the game page Object
boardGame.addEventListener('click',shuffle);//this for the game page Object



}

לייק 1

לא מצליח להבין למה ההשמה של this.el_ex - לא עובדת בתוך הפונקציה start של GamePage- כששמתי נקודת עצירה הפקודה כתובה נכון - אבל משום מה הוא מדלג על זה ?
קובץ 1 : Main.js

var el =document.getElementById("main");
var app = new Application(el);

קובץ 2:Applicaion.js

function Application(el){
    this._el=el;
    
   
    window.addEventListener("hashchange",this._ShowPageFromHash.bind(this));
    this._ShowPageFromHash();
}


Application.prototype._GetPage = function(hashName){
    if(hashName==="Help"){
       return HelpPage;
    }
    else{
        return GamePage;
    }
};

Application.prototype._getNewPage = function(page){
    if(page===GamePage){
        if (!this.game){
            this.game=new page;
        }
        return this.game;
    }else{
        return new page;
    }
};

Application.prototype._ShowPageFromHash = function(){
     var hashName = window.location.hash.substr(1);
     var page=this._GetPage(hashName);
     
    this._ShowPage(page);
};

Application.prototype._ShowPage = function(page){
    //exit from the old page
    if(this.activePage){
        this.activePage.exit();
    }
    
    var p = this._getNewPage(page);
    var html = document.getElementById("t-" + p.template).innerHTML;
    this._el.innerHTML=html;
    
    this.activePage = p;
    p.start(this._el);
};

קובץ 3 :Page.js

function Page(template){
   this.template=template;
}

Page.prototype.start =function(el){ this._el = el;};
Page.prototype.exit =function(){};

קובץ 4:HelpPage.js

function HelpPage(){
    Page.call(this,"HelpPage");
}

HelpPage.prototype=Object.create(Page.prototype);

קובץ 5:GamePage.js

function GamePage(){
    Page.call(this,"GamePage");
    
    this.Score =0;
    
}

GamePage.prototype=Object.create(Page.prototype);

GamePage.prototype._refreshGame = function () {
    this.indxWinner = Math.floor(Math.random() * divs.length);
    this.LoadGame();
};

GamePage.prototype.LoadGame = function () {
    var win = this.el.querySelector(".game .winner");
    win.classList.remove("winner");
    
    var index = Math.floor(Math.random() * divs.length);
    divs[index].classList.add("winner");
    
    this.el_ex.p.textContent = this.Score;
    
};

GamePage.prototype.setScore= function (event) {
    var s = event.target;
    if (!s.parentElement===game){
       return;
   }
   
   if (s.classList.contains("winner")) {
       this.Score+=5;
   }
    else{
        this.Score -=5;
    }
   
    this.refreshGame();
};

GamePage.prototype.start = function(el){
    Page.prototype.start(el);
    
    this.el_ex = {
                root: el,
                game: el.querySelector(".game"),
                divs: el.querySelector(".game div"),
                p: el.querySelector(".score span")
    };
    
    this.el_ex.game.addEventListener("click",this.setScore.bind(this));
};

קובץ 6: index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Get The Red With single application</title>
        <link rel="stylesheet" href="style.css" />
    </head>
    <body>
        <div id="main"></div>

        <!--Game Page-->
        <script type="text/template" id="t-GamePage">
            <p class="score">Score: <span>0</span></p>
            <div class="game">
                <div class="winner"></div>
                <div></div>
                <div></div>
                <div></div>
            </div>
            <p><a href="#Help">Help...</a></p>
        </script>

        <!--Help Page-->
        <script type="text/template" id="t-HelpPage">
            <h1>This is the Help Page</h1>
            <textarea rows="4" cols="50">
                At w3schools.com you will learn how to make a website. We offer free tutorials in all web development technologies.
            </textarea>
            <p><a href="#Game">Return to game...</a></p>
        </script>
        
        <script src="JS/Applicaion.js"></script>
        <script src="JS/Page.js"></script>
        <script src="JS/GamePage.js"></script>
        <script src="JS/HelpPage.js"></script>
        <script src="JS/Main.js"></script>
    </body>
</html>

קובץ 7:style.css

.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;
}

תצטרך לפרט קצת יותר - מה הכוונה לא עובדת? ומה הכוונה מדלג?

קובץ 5 בפונקציה start - הגדרתי שם אובייקט שמפרק את האלמנט - והקוד מגיע למשתנה הראשון root - ואח"כ מדלג על שאר המאפיינים ולא מאכלס אותם - ואז נופל בשורה הבאה כי הערך ריק
ולכן game ריק

this.el_ex = {
                root: el,
                game: el.querySelector(".game"),
                divs: el.querySelector(".game div"),
                p: el.querySelector(".score span")
    };

אם אתה שם נקודת עצירה בדיוק לפני ההשמה האם הפונקציה:

el.querySelector(".game")

מחזירה את מה שאתה חושב שהיא צריכה להחזיר?
(אפשר לבדוק את זה ב console כשהדפדפן עוצר בנקודת העצירה)

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

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

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

See the Pen dqxBRg by yonig (@yoniGoren) on CodePen.

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

בינתיים מצאתי שתי טעויות בקוד:

סדר הכתיבה חשוב - את שתי השורות בהתחלה שיוצרות את האוביקטים (שקוראות ל new) צריך להפעיל אחרי הגדרת כל הפונקציות.

ב JavaScript זה עובד:

var result = foo();
function foo() { return 5; }

אבל זה לא עובד:

var result = foo();
var foo = function() { return 5; };

למרות ששני הקטעים נראים דומים - בגירסא הראשונה באופן אוטומטי JavaScript מבצע את הגדרת הפונקציה לפני כל שאר הקוד (זה נקרא Hoisting) אבל בגירסא השניה אין את ההתנהגות הזו - ולכן בגירסא השניה מנסים להפעיל את הפונקציה לפני שהוגדרה.

בקוד שלך תיקון ראשון יהיה להזיז את שתי השורות הראשונות לסוף הקוד.

בעיה שניה היא בפונקציה setScore בשורה:

    if (!s.parentElement===game){

לא קיים משתנה game בנקודה זו.

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

אגב כאן בפורום זה יוצא יותר טוב כשאתה מדביק רק את ה URL מקודפן - לא צריך ללחוץ Embed.

לייק 1

image

זה מריץ בהתלחלה את ה- start - אבל מגיע לנקודה הזו בקוד ומדלג על השורות האלו במבנה של האובייקט - זה משהו שלא תקין בחלק הזה - ואני לא מצליח להבין מה ? כשאני לוקח את הביטוי אחרי הנקודיים - הוא מחזיר תשובה נכונה ?
נ.ב : תיקנתי את הקוד ועשיתי את מה שכתבת למעלה ועדיין זה פשוט מדלג על ההגדרה הזו משום משום מה …

זאת השגיאה שאני קיבלתי כשניסיתי את הקוד שלך (עשיתי fork לקודפן כי היתה שם שגיאת תחביר)

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

לקחת את הגירסא הלא נכונה - אני כבר תיקנתי את זה
זאת הגירסא הנכונה - ועדיין מדלג לי על הקוד …

זאת השגיאה שקפצה לי בגירסא האחרונה שלך

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

ל JavaScript יש נטיה להיות מבלבל. הפונקציה:

document.querySelector('...')

מחזירה אלמנט בודד ואילו הפונקציה עם השם הממש דומה:

document.querySelectorAll('...')

מחזירה מערך.

נראה לי שבשביל לקחת את divs בחרת את הפונקציה הלא נכונה…

Blockquote

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