I have the following code:
function expandTextarea () {
var textareas = document.getElementsByTagName('textarea');
for (var i = 0; i < textareas.length; i++) {
if (textareas[i].scrollHeight > 100) {
textareas[i].style.height = textareas[i].scrollHeight + 2 + 'px';
} else {
textareas[i].style.height = '100px !important';
}
textareas[i].addEventListener('onchange', adjustTextareaHeight(this));
// textareas[i].addEventListener('keyup', adjustTextareaHeight(textareas[i]));
// textareas[i].addEventListener('paste', adjustTextareaHeight(textareas[i]));
// textareas[i].addEventListener('cut', adjustTextareaHeight(textareas[i]));
}
}
function adjustTextareaHeight (textarea) {
console.log(textarea.length);
if (textarea.length > 0) {
textarea.style.height = textarea.scrollHeight + 2 + 'px';
}
}
For some reason the event listener is never fired. Note that expandTextarea is called from my window.onload = function () {} function.
When I call expandTextarea directly it runs without any issues but when I make a change, pressing enter a bunch, it never calls the onchange event.
You have to pass a function to addEventListener. Currently you are passing undefined, the return value of adjustTextareaHeight. So the browser doesn't know what to execute when the event occurs.
What you want is:
textareas[i].addEventListener('change', function() {
adjustTextareaHeight(this);
});
In your code you are calling adjustTextareaHeight immediately (not on change). this likely refers to the global object (window) and window.length is undefined, which is not > 0.
Related
I want to check if div I click has a class called "mole". I add the a callback with a property event, when adding click addEventLister for all classes called "cell".
Game.prototype.checkClicks = function(e) {
console.log(e);
if (e.classList.contains("mole")) {
new Audio(self.baseUrl + self.audio[0]).play();
self.score++;
self.displayScore();
} else {
new Audio(self.baseUrl + self.audio[1]).play();
}
};
const myAudio = document.createElement("audio");
myAudio.setAttribute("src", "./sounds/el_jarabe_tapatio.m4a");
myAudio.loop = true;
$(document).ready(function() {
const moleGame = new Game();
moleGame.startGame();
const allCells = document.getElementsByClassName("cell");
for (let i = 0; i < allCells.length; i++) {
allCells[i].addEventListener("click", moleGame.checkClicks(event));
}
});
I get the following error:
Uncaught TypeError: Cannot read property 'classList' of undefined
at Game.checkClicks (main.js:110)
at HTMLDocument.<anonymous> (main.js:128)
at j (jquery-3.2.1.min.js:2)
at k (jquery-3.2.1.min.js:2)
means, "e" the event is not defined.. I understand the click event is not defined on load, but only when I click on the div, but how can I fix this?
You're calling checkClicks immediately instead of passing a function that'll be called when the event happens.
Furthermore, you're expecting a classList on an event object, which won't be there.
Lastly, you're using self, in an apparent hope that it will be referencing the game object.
Instead pass a function as the handler, use the currentTarget property of the event object, and use this instead of self.
Game.prototype.checkClicks = function(e) {
console.log(e);
if (e.currentTarget.classList.contains("mole")) {
new Audio(this.baseUrl + this.audio[0]).play();
this.score++;
this.displayScore();
} else {
new Audio(this.baseUrl + this.audio[1]).play();
}
};
const myAudio = document.createElement("audio");
myAudio.setAttribute("src", "./sounds/el_jarabe_tapatio.m4a");
myAudio.loop = true;
$(document).ready(function() {
const moleGame = new Game();
moleGame.startGame();
const allCells = document.getElementsByClassName("cell");
for (let i = 0; i < allCells.length; i++) {
allCells[i].addEventListener("click", event => moleGame.checkClicks(event));
}
});
You are invoking the handler immediately, instead of passing the handler for execution later (when the event occurs). So remove the parentheses. The call will be made with the event object as argument:
allCells[i].addEventListener("click", moleGame.checkClicks);
If your checkClicks method relies on this being moleGame (which seems not to be the case), then do:
allCells[i].addEventListener("click", moleGame.checkClicks.bind(moleGame));
Note that although this will define e, the event object does not have a classList property. You maybe intended e.target.classList.
I have the following function that works in Chrome/IE:
for(var i = 0; i < 5; ++i) {
document.all["elems" + i].onscroll = getFunc("onScrollAdj(document.all['tb" + i + "']);");
}
function getFunc(jsString) {
return new Function(jsString);
}
However I get the error:
ReferenceError: event is undefined.
I have tried to re-write the function to include an event however I then get another error:
var i is undefined.
document.all["elems" + i].onscroll = onScrollAdj(event, document.all['tb" + i + "');
Is there any way to ensure both event and attributes can be passed?
however I get 'ReferenceError: event' is undefined.
That's because you're trying to use event without declaring it as an argument. That only works on Microsoft browsers, which make event a global, and Chrome which throws a bone to Microsoft-only code. You will need to declare the argument.
There's no need for new Function virtually ever, and certainly not in this case:
for(var i = 0; i < 5; ++i) {
document.all["elems" + i].onscroll = getFunc(i);
// ------------------------------------------^
}
function getFunc(i) {
// --------------^
return function(event) { onScrollAdj(event, document.all['tb' + i]); };
// -------------^--------------------^
}
Note that you'll have to ensure that onScrollAdj accepts event:
function onScrollAdj(event, tbThingy) {
// ------------------^
I have the following piece of code:
var i = 0;
for (var anchor in window.globals.html.anchors) {
var value = window.globals.html.anchors[anchor];
value.addEventListener('click', function(i) {
var currData = window.globals.data[i],
data_clientId = 0;
if (currData.client_product_id !== undefined) {
data_clientId = currData.getAttribute('data_clientId');
} else if (currData.product_id !== undefined) {
data_clientId = currData.product_id;
}
Statistics.send(data_clientId);
window.open(window.globals.data[i].url, '_blank');
}(i));
i++;
}
Which means I want to access global array by interator inside the click event listener. If I don't pass no i to the click event I get the maximum number possible in each iteration, as i would be a global variable.
But right here it seems that all iterations of click events are invoke before clicking anything, onload.
Why is that so, what's wrong?
Looks like you are calling the handler whilst trying to add it to addEventListener. You need to wrap it in a function thus creating a closure that encapsulates your i variable.
var i = 0;
for (var anchor in window.globals.html.anchors) {
var value = window.globals.html.anchors[anchor];
value.addEventListener('click', (function(i) {
return function() {
var currData = window.globals.data[i],
data_clientId = 0;
if (currData.client_product_id !== undefined) {
data_clientId = currData.getAttribute('data_clientId');
} else if (currData.product_id !== undefined) {
data_clientId = currData.product_id;
}
Statistics.send(data_clientId);
window.open(window.globals.data[i].url, '_blank');
}
}(i)));
i++;
}
Notice now that the function you are passing to addEventListener is invoked immediately and returns a function itself where your variable i is scoped.
I'm trying to move a div up and down using two keys in Javascript. The idea is that while a certain key is depressed, a function loops and adds to the div's 'top' style value each time. The basic function works, but I can't get it to loop and I can't get anything to respond to a keypress.
It's hard to find info on keypress handling in Javascript, it seems most people use jQuery to handle that.
Is my use of the do-while loop correct? is there a better way to handle keydown and keyup events?
Here's my code:
var x = 0;
console.log(x);
function player1MoveDown() {
var value = document.getElementById("player1").style.top;
value = value.replace("%", "");
value = parseInt(value);
value = value + 1;
value = value + "%";
document.getElementById("player1").style.top = value;
console.log(value);
} //moves paddle down; adds to paddle's 'top' style value
function player1MoveSetting() {
x = 1;
do {
setInterval(player1MoveDown(), 3000);
}
while (x == 1);
console.log(x);
} //paddle moves while x=1; runs player1MoveDown function every 3 seconds
function player1Stop() {
x = 0;
}
And here's the relevant bit of HTML:
<div class="paddle" id="player1" style="top:1%" onkeydown="player1MoveSetting()" onkeyup="player1Stop()"></div>
You cannot attach a keydown event to a div, unless it has a tabindex:
<div class="paddle" id="player1"
onkeydown="player1MoveSetting()"
onkeyup="player1Stop()"
tabindex="1"
>
</div>
You can replace all this code:
var value = document.getElementById("player1").style.top;
value = value.replace("%", "");
value = parseInt(value);
value = value + 1;
value = value + "%";
document.getElementById("player1").style.top = value;
… with this:
var p1= document.getElementById('player1');
p1.style.top= parseInt(p1.style.top)+1+'%';
This calls the return result of player1MoveDown:
setInterval(player1MoveDown(), 3000);
Since player1MoveDown doesn't return anything, it's the equivalent of
setInterval(null, 3000);
To call the function every 3 seconds, do this instead:
setInterval(player1MoveDown, 3000);
This creates an infinite loop:
x = 1;
do {
setInterval(player1MoveDown, 3000);
}
while (x == 1);
Even though keyup will set the global x to 0, it will never run because the loop never ends.
Instead, create a timer variable, which is set on keydown and cleared on keyup.
Complete JavaScript Code
var timer;
function player1MoveDown() {
var p1= document.getElementById('player1');
p1.style.top= parseInt(p1.style.top)+1+'%';
console.log(p1.style.top);
}
function player1MoveSetting() {
if(timer) return;
timer= setInterval(player1MoveDown, 100);
}
function player1Stop() {
clearInterval(timer);
timer= null;
}
document.getElementById('player1').focus();
Working Fiddle
Thanks to the help of you fine Overflowians, I fixed up my silly little RNG Addition game and got it working. Now, at one user's suggestion, I'm trying to change the scope of the addition game's code from global to local before I code up the next game; I want each game to be completely contained within its own scope, as I understand that learning to not thoughtlessly contaminate the global scope is a good idea. However, I'm a bit stuck on how to achieve that.
Here's the code for the currently functional addition game:
function beginAdditionChallenge() {
var x = Math.ceil(Math.random()*100);
alert(x);
for (var i = 0; i < 3; i++) {
var a = Number(prompt("Provide the first addend.", ""));
var b = Number(prompt("Provide the second addend.", ""));
if (a + b === x) {
alert("Well done!");
break;
}
else if (a + b !== x && i < 2) {
alert("Please try again.");
}
else {
alert("Derp.");
}
}
}
function initChallenge() {
var button = document.getElementById("challengeButton");
button.addEventListener("click", beginAdditionChallenge);
}
window.addEventListener("load", initChallenge);
And here's my attempt to wrap it, which only succeeds in breaking the game, such that the button doesn't even respond:
window.addEventListener("load", function() {
function beginAdditionChallenge() {
var x = Math.ceil(Math.random()*100);
alert(x);
for (var i = 0; i < 3; i++) {
var a = Number(prompt("Provide the first addend.", ""));
var b = Number(prompt("Provide the second addend.", ""));
if (a + b === x) {
alert("Well done!");
break;
}
else if (a + b !== x && i < 2) {
alert("Please try again.");
}
else {
alert("Derp.");
}
}
}
function initChallenge() {
var button = document.getElementById("challengeButton");
button.addEventListener("click", beginAdditionChallenge);
}
window.addEventListener("load", initChallenge);
});
Functional code is available on JSFiddle here.
Note that the onLoad option in JSFiddle does the same as your 2nd snippet. You'll want to choose one of the No wrap options when binding to 'load' yourself.
And, the issue stems from attempting to bind to 'load' within a 'load' handler:
window.addEventListener("load", function() {
// ...
window.addEventListener("load", initChallenge);
});
When the event is already firing and handling the outer binding, it's too late to add more handlers to it. They won't be cycled through and the event shouldn't occur again.
You'll either want to call initChallenge within the outer event binding:
window.addEventListener("load", function() {
// ...
initChallenge();
});
Or, you can use an IIFE with the inner binding:
(function () {
// ...
window.addEventListener("load", initChallenge);
})();