So I'm working on a game that will accept user input (up down left right arrow keys). To do so, I will add an event listener that will check when the user presses said keys. I have it set up in the following way:
function gameStart(){
//Generates four random numbers on board
for(gameStart_i = 0; gameStart_i < 4; gameStart_i++){
generateNumber();
}
document.getElementById("start_button").innerHTML = "Reset Game";
document.getElementById("start_button").setAttribute('onclick', 'reset()');
document.getElementById("start_button").id = 'reset_button';
var board = document.getElementById("game_background");
console.log("1");
board.addEventListener("keydown", inputListen(), false);
console.log("1.1");
}
function inputListen(e){
console.log("2");
var code = e.keyCode;
switch(code){
case 37:
alert("left");
break;
case 38:
alert("up");
break;
case 39:
alert("right");
break;
case 40:
alert("down");
break;
}
}
This seems in line with how tutorials show it. However, some tutorials change the addEventListener line with something that would look like this:
board.addEventListener("keydown", inputListen, false); //removes the () after the function
However that doesn't seem to work for me when I look into my console.
When I run my code my console gives me the following error:
1
2
Uncaught TypeError: Cannot read property 'keyCode' of undefined
at inputListen (script.js:86)
at gameStart (script.js:16)
at HTMLButtonElement.onclick (2048Game.html:114)
I think it's because I don't pass any parameters to my function, but none of the online tutorials pass paraements in their addEventListener statement.
Thank you,
Alex
The proper way to do this is indeed to remove the () after inputListen.
Using the () immediately calls the function, which then gives you aforementioned Cannot read property 'keycode' of undefined since no input parameters were given.
You should have also received the error in the console between the two console.log lines, which proves the error came from the addEventListener line.
You want to pass the function without calling it using the line you posted:
board.addEventListener("keydown", inputListen, false); //removes the () after the function
Take a look at this JSFiddle:
function gameStart(){
console.log("before");
document.getElementById("game_background").addEventListener("keydown", inputListen, false);
console.log("after");
}
This should print out to the console
before
after
And then nothing else until it detects a keydown.
It also matters what kind of element you bind this to. An element such as <input> will have no trouble here, but normal non-focusable elements will need the tabindex attribute in order to be focused and respond to a keydown:
<div id="game_background" tabindex="-1">
</div>
You'll need to click the element once to focus onto it, then your events should be captured. More info at this answer.
Yes drop the (), you are using the function itself to the event listener, not the result of the function call.
The keydown event is likely not sent to an arbitrary <div> in the page. Add the event listener to the window itself instead, if you don't want the user to manually click to focus on it.
window.addEventListener('keydown', inputListen);
Related
I keep reading that there is no difference between the two but I am writing a quiz app and am seeing some differences
For example:
Quiz.prototype.handleGuess = function (id, guess) {
let button = document.getElementById(id);
button.addEventListener('click', function(){
quiz.guess(guess);
console.log(guess);
}
if(!quiz.hasEnded){
quiz.displayNext();
} else {
quiz.displayScore();
};
};
When using an event listener, I will log the guess to the console on the first button choice. When I choose an answer for the second question the console will read not only the new selection but also the choice I made from the previous question. This does not happen when using .onclick() and I am not sure why!
Consider the following code:
var el1 = document.getElementById("someEl1"),
el2 = document.getElementById("someEl2");
function firstHandler() {
alert("First handler");
}
function secondHandler() {
alert("Second handler");
}
el1.addEventListener("click", firstHandler);
el1.addEventListener("click", secondHandler);
el2.onclick = firstHandler;
el2.onclick = secondHandler;
<div id="someEl1">First Element</div>
<div id="someEl2">Second Element</div>
In case 1, clicking on el1 will alert with both messages. In case 2, clicking on el2 will only alert with the second because we overwrote what onclick does with the second assignment.
addEventListener effectively assigns a callback to some internal array of listener callbacks that will all be called whenever the event is triggered.
onclick is a single property with a single value. When you assign to it, the old value is replaced by the new assignment.
I would highly suggest that you do not use the onclick method. It makes code harder to maintain. If you are in a large code base and you set the onclick of an element and then later on another coder also sets the onclick without knowing that that element already had its onclick set, then you will run into a difficult time trying to figure out why your code is broken all of a sudden. Using the event listener pattern makes for more extensible and decoupled code.
I started to make a text game, the basic idea is:
When the game starts, user have to input his choises into the input field. The text he inputs, interacts with a switch inside the function and if user inputs the "right commands", it calls another function that may include another switch and so on.
I have a bug in this code:
The switch inside the first function is working well, and when its first case(case "yes") calls the another function, everything is starts working just fine. But when user inputs text that should interact with the switch of the second function, he actually still interacts with the first function's switch(and also with the second).
Would anybody be willing to help me to solve it or even point me in the right direction?
var startGame = function(){
showText("Welcome to the virtual pseudo-reality")
showText("Are you ready to start your journey?")
$('#btn').click(function(e) {
e.preventDefault();
switch ($('input[name=myInput]').val().toLowerCase()) {
case "yes": showText("- "+$('input[name=myInput]').val());
showText("Welcome aboard!");
selectChar();
break;
case "no": showText("- "+$('input[name=myInput]').val());
showText("Your choise is no.");
break;
default:
showText("- "+$('input[name=myInput]').val());
showText("Sorry, wrong input.") ;
break;
};
$('input[name=myInput]').val('');
var element = document.getElementById("storyBoard");
element.scrollTop = element.scrollHeight;
});
};
//A first in-game function, needs to select the character that user want to be:
var selectChar = function(){
showText("<br>"+"Choose who you are, you have to choose one of two heroes: Phantomorph and Disogr.");
$('#btn').click(function(e) {
e.preventDefault();
switch ($('input[name=myInput]').val().toLowerCase()) {
case "phantomorph": showText("- "+$('input[name=myInput]').val());
showText("You choosed Phantomorph.");
user = phantomorph; friend = disogr;
break;
case "disogr": showText("- "+$('input[name=myInput]').val());
showText("You choosed Disogr.");
user = disogr; friend = phantomorph;
break;
default: alert("You need to make a right choose.");
selectChar();
break;
};
$('input[name=myInput]').val('');
var element = document.getElementById("storyBoard");
element.scrollTop = element.scrollHeight;
});
};
startGame();
You are binding multiple events to the same input, so that when a user clicks the second time both event handler callbacks are executed.
Since you are using jQuery, I would switch from using .click, to .on('click', and then in the switch case where you bind the second click handler you can use .off('click' to remove the initial handler.
Better still have a variable to track the state of your game, and move the binding of second event handler out of the switch case. You'll need to do this otherwise you will be rebinding click handlers every time a user clicks.
[EDIT]
You can add any number of event handlers at once, so long as the DOM element is ready (in the jQuery document ready callback), or bind just one click event and include logic inside the event handler that checks what data you've already captured from the user to trigger any arbitrary behaviour.
I'd suggest you create one click handler and inside the callback check the value of your user variable to see if the user has set a value for it yet. (You'd just need to ensure the scope of that variable is accessible to your click handler, and any part of your application that needs it, currently in your example there is no var user;)
I have two buttons. Save and undo. They execute a javascript, and in the end they call resetButtons() which is meant to disable the buttons until any other change in the users input occurs. This code has worked for over a year now, but while implementing some new features, this one has broken.
Here is a direct snippet of code:
function undo(){
var r = confirm("Are you sure you want to cancel your inputs?");
if (r == true) {
setLoadedValues(); //calls in the cached previous values
updateAllBoxValues(); //updates view
console.log("before resetting buttons");
resetButtons();
console.log("after resetting buttons");
}
}
function resetButtons(){
console.log("resetting buttons");
$(".save").addClass("disabled");
$('.save').attr('disabled', 'disabled');
$(".undo").addClass('disabled');
$('.undo').attr('disabled', 'disabled');
console.log("done resetting buttons");
}
This gives the following output:
before resetting buttons
after resetting buttons
I have suspected some wrong formatting with brackets, but this is a direct snippet of code, and if undo() works, so should resetButtons(), right? Also, I should have been given a undefined function, or an unexpecetd end of input or something similar if that was the case. The call to resetButtons is also ignored when I click the save button. I am lost. Help?
You have a duplicate function definition. Rename one of them.
Your code seems fine, you must have some other error somewhere else (like a function named like those two).
By the way, I suggest using jQuery's "prop" method to change the disabled attribute:
$('foo').prop('disabled', true);
$('foo').prop('disabled', false);
Try triggering the resetButton() function in console, if there's an issue it should return it. Testing it in a random console gives all the logs it should, but I can't vouch for the selectors. The error is probably elsewhere in the code.
My objective is very simple: I would like to intercept right-key and left-key press and, accordingly, execute a particular JavaScript. In particular, I have two img elements in my HTML that are basically a left and a right arrow to change a central picture:
<img id="goleft" onclick="changePic(this)" src="x"/>
<img id="goright" onclick="changePic(this)" src="y" />
The function changePic(this) is hence capturing the caller object (either left or right arrow) and changing the source accordingly. However, for sampling purpose, let's say here that it will just show in an alert the id of the object:
function changePic(obj) {
alert("hello world");
alert(obj.id);
}
Everything works fine until here. On click event, the Javascript is properly called. However, now I'm implementing a system to capture the key-right and key-left and executing the same action. The following function will capture any key-press event:
document.onkeydown = function (evt) {
evt = evt || window.event;
switch (evt.keyCode) {
case 37:
leftArrowPressed();
break;
case 39:
rightArrowPressed();
break;
}
};
...and, clearly, the two functions "leftArrow" and "rightArrow", I thought, could have simply done this:
function leftArrowPressed() {
changePic(document.getElementById("goLeft"));
}
function rightArrowPressed() {
changePic(document.getElementById("goRight"));
}
However, this does not work. The functions leftArrowPressed and rightArrowPressed are properly called (tested with an alert) but the function changePic is called (because I see the alert "hello world" popping-up but it fails on "obj.id" call). So I believe this issue is related to the way I pass the object from the caller to the listener function: could anyone please tell me what I'm doing wrong?
P.s. I'm a newbie of this language, so please don't hesitate to remark me anything that I've thought is unrelevant for answering the question but actually is not.
Ids are case sensitive, your id is goleft but you try to look it up with goLeft.
I am going to suggest a minor change to your code that might make your life easier:
<img id="goleft" onclick="leftArrowPressed()" src="x"/>
<img id="goright" onclick="rightArrowPressed()" src="x"/>
Change your changePic() function to take a string instead of an element and this will make things a little easier on you.
If you want to stick to your current implementation: you use goLeft in the lookup and it should be goleft. Case sensitive!
This is my html code
Hit
This is my javascript file
function clickHandler(evt) {
var thisLink = (evt)?evt.target:Window.event.srcElement;
alert(thisLink.innerHTML);
return false;
}
But when i click the Hit Link, it redirects.
you need to pass in the event if you wish to preventDefault.
html:
Hit
script:
function runFunction (evt) {
evt.preventDefault();
evt.stopPropagation();
}
To tie both of the very-correct answers together, what's happened is you've inlined a function where you've written onclick="return runFunction();"
If you look at that, what it's really doing is going like this:
var link = document.getElementById("myLink");
link.onclick = function () { runFunction(); };
See the problem?
My runFunction is being called without any event object passed in, at all.
...which means that var thisLink = (evt) ? is going to return false, which means that it's going to try to run in oldIE-mode.
By writing onclick="runFunction", that's the same as saying:
link.onclick = runFunction;
Which means that when the onclick event happens, runFunction will be called, and in W3C-compliant browsers, it will be sent an event object.
Which is why that solution works.
The best way to avoid a lot of this confusion is to deal with JavaScript from inside of JavaScript, and to deal with HTML inside of HTML, so that you don't have to worry about how strings translate into code.
Now, to get all of this to work, AND prevent redirection, you want to do this:
for W3C browsers (the ones that pass the event parameter):
function runFunction (evt) {
// stops the default-action from happening
// means you need to find another way to fire it, if you want to later
evt.preventDefault();
// stops higher-up elements from hearing about the event
// like if you stop a submit button from "clicking", that doesn't stop the form
// from submitting
evt.stopPropagation();
//the oldIE versions of both of these are
event.cancelBubble = true;
event.returnValue = false;
}
When I plugged your code into chrome, I got this as the error in the console:
Uncaught TypeError: Cannot read property 'srcElement' of undefined
IF the javascript bombs out while processing, it never gets a chance to return at all so the browser tends to disregard what is in the onclick handler after the exception.
Since it bombed out... default behavior of anchor tags, which is to send you off to wherever the href says to go.
Try wrapping the contents of the function in a try/catch block and see what turns up if this kind of thing plagues you.