简体   繁体   中英

First time using javascript for a clicker game that doesn't work

this is my first time using javascript. I basically wanted to create a "clicker" game. The target of the game is clicking an image to make points. I've tryed fixing it by myself and looking on the internet but I found nothing. What's is cauising the problem ?

Basically everytime I open the page the score is already set to 1 but when i click on the image it does not increases.

<div id="bgk">
<div class="layer">
        <div class="row p-2">
            <div class="col-lg-6 col-xl-6 text-left"><p>Score: <span class="score"><script>document.write(scorecounter());</script></span></p></div>
            <div class="col-lg-6 col-xl-6 text-right"><p>Time: <span class="time">2</span></p></div>

            <div class="p-2">
                <img src="png.png" id="image" onclick="scorecounter()">
            </div>
        </div>
    </div>
</div>

While this is the script that increases the score

<script>
var score = 0;
function scorecounter() 
{
    score ++;
    return score;
}
</script>

Your score is initially one because you call your function when you use

document.write(scorecounter());

This will increase the value of score then return it, thus displaying the number 1.

The reason why your click doesn't increase the value of score is because you're not displaying the score again. At the moment you are simply just displaying the score when the page loads. Then when you click your image the value of score is increased but you're not displaying it to the DOM (ie the page) again. To display the value of score to the page you can use .innerHTML on the element where you want the score to appear.

See a working example below:

 var score = 0; function scorecounter() { score++; document.getElementById('score').innerHTML = score; // Set the element with the id 'score' to have the value 'score' inside of it. Also note that .innerHTML can be replaced with .textContent to help resolve XSS vulnrabilities. } 
 /* css - ignore this */ .red { background-color: red; height: 50px; width: 100%; color: white; text-align: center; cursor: pointer; } 
 <p>Score: <span id="score">0</span></p> <div class='red' onclick="scorecounter()"> Click me (imagine this as your image) </div> 

Edit: As @BrandonBuck pointed out, .innerHTML is prone to cross-site scripting (XSS). To help avoid this using .textContent instead of .innerHTML can help avoid this.

Here is a JSFiddle of it working.

So there are quite a few things you're doing that you probably shouldn't be doing. And it's not necessarily your fault. JavaScript is old, and there are a lot of resources online and some of those resources are bound to be bad. Not to mention, I have no idea where you started from so you could have been given some really old example or what-have-you.

So where to begin, let's see... we don't use document.write anymore. As a matter of fact, when I started using JS about 17 years ago it was not really that common. The problem with document.write is that it's not doing what you think it's doing. When the page is initially parsed that will be executed, and it's going to write whatever you told it to, and that's it. It's done its job and it's over. It will never write again until you reload the page.

As @NickParsons pointed out in his comment, your score "starts" at 1 because you're calling scorecounter in your write statement, so you're incrementing the score and writing it's new value at the same time.

Next, you're using HTML attributes to associate JavaScript actions. This is generally frowned upon in modern web development as we tend to like to keep our JS and HTML pretty separate. I'll show you some examples of how to do that.

Okay, so another thing is global values. Of course in your example it's not necessarily avoidable but we can do some cleaning up for that. So let's take a go. First, let's clean up the HTML. Most of it (aside from the JS) is not bad at all.

this is my first time using javascript. I basically wanted to create a "clicker" game. The target of the game is clicking an image to make points. I've tried fixing it by myself and looking on the internet but I found nothing. What's is causing the problem?

Basically everytime I open the page the score is already set to 1 but when I click on the image it does not increase.

<div id="bgk">
  <div class="layer">
    <div class="row p-2">
      <div class="col-lg-6 col-xl-6 text-left">
        <p>
          Score: <span class="score-value"></span>
        </p>
      </div>
      <div class="col-lg-6 col-xl-6 text-right">
        <p>
          Time: <span class="time-value">2</span>
        </p>
      </div>
      <div class="p-2">
        <img src="png.png" id="image">
      </div>
    </div>
  </div>
</div>

So the biggest thing I've done outside of formatting is remove all the JS code, and change the class on the value spans to <name>-value , so score is a perfectly fine class name, don't get me wrong -- but score-value is more clear about what we're going to put there.

Next thing we need to do is clean up the JavaScript. As a base, it's not bad. But it's not where we want to go. Let's do this in steps. I'm going to start from scratch here since we're going to change it quite a bit. First thing is we have a Game , and our game will have a score associated with it. So let's make a class (class syntax is new, but supported natively by the majority of major browsers in use).

class Game {
  constructor() {
    this.score = 0;
  }
}

Bam. Got our class. What else do we need? Well -- we need to increment our score. So let's do that.

class Game {
  constructor() {
    this.score = 0;
  }

  incrementScore() {
    this.score += 1;
  }
}

Easy enough. Next, we need to render our score, or add it to the DOM (the HTML after it's been processed by the browsers becomes the "DOM" or Document Object Model, which is an API that lets us interact with the page programmatically).

Now, rendering this score could be part of our game, but it's not really -- our game is supposed to be just the game itself. It really shouldn't contain a bunch of other logic. Otherwise we get a super class and we may as well have not tried to do this. But we can make a new class... let's call it UserInterface , since, y'know, it's our UserInterface . Most JavaScript libraries will refer to the HTML/DOM aspect of your app as a "view" or "component," I'm using "user interface" since we're talking about a "game" and the common term there is UI.

class UserInterface {}

Okay, so we need to render text . To do that, we need to fetch the DOM node we want to store the score , and then we need to set it's text to the current score . The UserInterface doesn't know that the score is, the Game does so we need to pass some data around.

class UserInterface {
  renderScore(score) {
    const el = document.querySelector('.score-value');
    el.textContent = score;
  }
}

You see I've used const . For the most part, you can imagine that const does that var does -- it declares a variable. Underneath they work quite differently. Without going in depth about JavaScript variable scoping let me just say that var is old school, and const is new school. const defines not only a variable like var but it also makes it so that it cannot be reassigned. Since we never reassign el in the body of renderScore I made it const . Now, var is old school as I said and its new school alternative is let , which does pretty much the same thing as const (as far as scope goes) but allows you to reassign the value. I encourage you to dive more into why let and const were added but that's beyond this answer.

There is an optimization here -- you see I'm searching for the .score-value element every time I render . This is bad. There are a few approaches I could take here but for the sake of simplicity, let's just do the easiest and get it up front.

class UserInterface {
  constructor() {
    this.scoreValueEl = document.querySelector('.score-value');
  }

  renderScore(score) {
    this.scoreValueEl.textContent = score;
  }
}

As an aside, you may not know what document.querySelector does, but you can think of it as searching the DOM (again, what the HTML page becomes after the browser processes it) for an element that matches the text you pass to it. The term selector comes from CSS, where you define your rules by "selecting" elements. You can select by tag, h3 , div , etc.. or you can select by class name (just append a . in front of the class name, like .score-value for class="score-value" elements) or you can select by an ID (just append a # similar to . for class names). You can get more complex but for now, leave it at that. So document.querySelector('.score-value') says, "Hey DOM, can you find some element that has a class "score-value" on it and give it back to me?"

Okay, so now I have the Game to track the score (and other future game elements) and then I have the User Interface to render this stuff to the user. Next, we need to do some interaction. This part can be quite a topic of passing around lots of functions, etc.. so I'm going to keep it relatively simple. With our Game and UserInterface class ready we'll just bind the click to our image and increment the score and render it. So let's give all that a go. First, let's create our new values:

const game = new Game();
const ui = new UserInterface();

// we want to show the score on load, so let's do that
ui.renderScore(game.score);

Okay, so first part down. Now let's handle clicks. Using document.querySelector again to fetch the image:

// see here, we only have one img tag on the page, so I use the tag
// as the selector to find it
const img = document.querySelector('img');

img.addEventListener('click', () => {
  game.incrementScore();
  ui.renderScore(game.score);
});

Okay, so two new things here. addEventListener is the JS way to say "hey element, when the user does X do Y", in this case we say "when the user clicks this image, run this function." So the first value we pass to addEventListener is the name of the event we want to handle and the second value is the function we want to execute when the event is triggered. Now that function, I wrote it as an "arrow function" which is shorthand and means the same thing as function() {} . So () => {} is the same thing as function() {} (there are, again, some differences I'm glossing over because it's out of scope for this answer and you should definitely look those up). So don't be alarmed by the arrow function there, it's simple a JS function we're passing in to call. Notice inside this function we increment the score and then render it.

So the final JS should be something like this:

class Game {
  constructor() {
    this.score = 0;
  }

  incrementScore() {
    this.score += 1;
  }
}

class UserInterface {
  constructor() {
    this.scoreValueEl = document.querySelector('.score-value');
  }

  renderScore(score) {
    this.scoreValueEl.textContent = score;
  }
}

const game = new Game();
const ui = new UserInterface();

ui.renderScore(game.score);

const img = document.querySelector('img');
img.addEventListener('click', () => {
  game.incrementScore();
  ui.renderScore(game.score);
});

Here is a JSFiddle of it working.

Add On

(yes sadly I have more to say)

All that being said, learning JS is easy and accessible. If you'd like to brush up or learn new things, you can head over to CodeAcademy, or if you don't mind some out of pocket expense SkillShare, PluralSight, CodeSchool, and many more offer JavaScript courses.

In addition to learning more about JavaScript, most real-world JavaScript is written with some framework as a starting point. Vue was mentioned to you, so as you feel you're knowledge and comfort increase with JS you should look into Vue. It's great because it features minimum boilerplate (the amount of code necessary to "get started") and offers, essentially, feature parity to other popular solutions. Other solutions are React, Angular, maybe Ember still exists, Meteor (for full stack stuff) and many more. Also, despite it being "on the way out" as more people move away from it, jQuery is still around and still provides a huge set of conveniences instead of working directly with the DOM. So there is always that route as well.

The world of JavaScript is too big , and still growing. So you can really find whatever you want for whatever purpose it serves. There's hundreds of MVC frameworks, component frameworks, DOM wrappers, other basic utils, game engines, and on and on and on.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM