Adding "this" to event listener? - javascript

I was creating a quiz application and decided to switch from .onclick() to .addEventListener(). In order to get that to work I had to add event handlers.
The only way I got the listeners to work was by adding the following code to the Quiz object constructor..
document.getElementById('guess0').addEventListener('click', this);
document.getElementById('guess1').addEventListener('click', this);
This works but I am not sure why. What exactly is the "this" doing in place as a function?
Entire page of code for reference:
function Quiz(questions) {
this.questions = questions;
this.score = 0;
this.currentQuestionIndex = -1;
document.getElementById('guess0').addEventListener('click', this);
document.getElementById('guess1').addEventListener('click', this);
this.displayNext();
}
Quiz.prototype.displayNext = function(){
this.currentQuestionIndex++;
if(this.hasEnded()){
this.displayScore();
this.displayProgress();
}else{
this.displayCurrentQuestion();
this.displayCurrentChoices();
this.displayProgress();
}
};
Quiz.prototype.hasEnded = function() {
return this.currentQuestionIndex >= this.questions.length;
};
Quiz.prototype.displayScore = function() {
let gameOverHtml = "<h1>Game is over!</h1>";
gameOverHtml += "<h2>Your score was: " + this.score + "!</h2>";
let quizDiv = document.getElementById('quizDiv');
quizDiv.innerHTML = gameOverHtml;
};
Quiz.prototype.getCurrentQuestion = function() {
return this.questions[this.currentQuestionIndex];
};
Quiz.prototype.displayCurrentQuestion = function() {
let currentQuestion = document.getElementById('question');
currentQuestion.textContent = this.questions[this.currentQuestionIndex].text;
};
Quiz.prototype.displayCurrentChoices = function() {
let choices = this.getCurrentQuestion().choices;
for (let i = 0; i < choices.length; i++) {
let choiceHTML = document.getElementById('choice' + i);
choiceHTML.innerHTML = choices[i];
}
};
Quiz.prototype.handleEvent = function(event){
if(event.type === 'click'){
this.handleClick(event);
}
};
Quiz.prototype.handleClick = function(event){
event.preventDefault();
let choices = this.getCurrentQuestion().choices;
if(event.target.id === "guess0"){
this.guess(choices[0]);
} else if(event.target.id === "guess1"){
this.guess(choices[1]);
}
this.displayNext();
};
Quiz.prototype.displayProgress = function() {
let footer = document.getElementById('quizFooter');
if (this.hasEnded()) {
footer.innerHTML = "You have completed the quiz!";
} else {
footer.innerHTML = "Question " + (this.currentQuestionIndex + 1) + " of " + this.questions.length;
}
};
Quiz.prototype.guess = function(choice) {
if (this.getCurrentQuestion().checkAnswer(choice)) {
this.score++;
}
};

You are making Quiz a "class" (as we normally think about classes, even if JS doesn't really have them). When you do quiz = new Quiz(questions), inside the Quiz constructor, this refers to the newly created Quiz object. addEventListener can accept one of two different values for the listener parameter:
This must be an object implementing the EventListener interface, or a JavaScript function.
Your Quiz implements the requisite interface by implementing handleEvent function. Thus, when you pass your newly-created quiz (as this) to addEventListener, you will get quiz.handleEvent invoked when the event happens.

Related

How to make loop not to continue until an event happens?

I am writing a function of a game:
function Game(){
while(true){
***
for(var i = 0; i < level; i++){
var color;
$(".btn").on("click", function(event) {
ButtonClickResponse(this.id);
color = this.id;
});
if(colorsOrder[i] != color){
GameOver();
return;
}
}
***
}
}
the "if statement" in the loop of function runs and increments "i" immediately many times when loop is started and doesnt wait for an above event to finish.
I searched for solving with "async await" and "promise" in google and stackoverflow, but didn't really understand how it worked so couldn't implemet it in my code.
This should work, although I didn't test it and you do things not in javascript way
async function Game() {
while (true) {
var level = 1;
$("#level-title").text("Level " + level);
var colorsOrder = [];
var randColor = GenerateRandomSquareColor();
colorsOrder.push(randColor);
ButtonClickResponse(randColor);
for (var i = 0; i < level; i++) {
var color;
// await for the click event
const event = await waitForClick($(".btn"));
// do stuff with the result
ButtonClickResponse(event.target.id);
color = event.target.id;
if (colorsOrder[i] != color) {
GameOver();
return;
}
level++;
}
}
}
function waitForClick(element) {
// create new promise
return new Promise((resolve) => {
const handler = function (event) {
// when button is clicked remove listener
element.off("click", handler);
// and resolve the promise
resolve(event);
};
// listen for click
element.on("click", handler);
});
}

How to call a function inside of another function in js Class [duplicate]

This question already has an answer here:
es6 call class methods from within same class
(1 answer)
Closed last month.
I looked on whole stack overflow but unfortunately, answer of this question wasn't available so I have a class and inside I have a function donutMaker and inside of this function I want to call another function which is autoClicker but it is not being called:
class DonutMaker {
constructor() {
this.donut_creater = document.getElementById("donut_creater");
this.donut_creater.addEventListener("click", this.donutMaker);
this.auto_clicker = document.getElementById("auto_clicker");
this.auto_clicker.addEventListener("click", this.autoClickerHandler);
}
donutMaker() {
this.selection = document.getElementById("donut_quantity");
this.quantity = this.selection.innerText;
this.updated_quantity = parseInt(this.quantity);
this.updated_quantity = this.updated_quantity + 1;
this.selection.innerText = this.updated_quantity;
}
autoClickerHandler = () => {
this.selection = document.getElementById("donut_quantity");
this.quantity = this.selection.innerText;
this.updated_quantity = parseInt(this.quantity);
this.new_quantity = this.updated_quantity - 1;
if (this.updated_quantity >= 1) {
this.selection.innerText = this.new_quantity;
this.quantity = this.new_quantity;
this.selection2 = document.getElementById("auto_clicker_quantity");
this.auto_clicker_quantity = this.selection2.innerText;
this.auto_clicker_quantity = parseInt(this.auto_clicker_quantity);
this.auto_clicker_quantity_updated = this.auto_clicker_quantity + 1;
this.selection2.innerText = this.auto_clicker_quantity_updated;
this.autoClicker;
} else {
console.log("Not Eligible");
}
};
autoClicker = () => {
console.log("Hello");
// console.log("Auto clicker");
};
}
let obj = new DonutMaker();
This line at the end of if supposed to call this.autoClicker but it isnt
You forgot to call it
this.autoClicker();
Take a look at the ‘Calling Functions’ section of the mdn docs.
Defining a function does not execute it but calling it this.autoClicker() does.

One function to recieve variables from many other functions

I 'm dealing an issue with some functions in javascript.
So i have this example
function(something) {
var example = 1;
var example2 = 2;
NameReturn(nameBusinessID, function(no) {
console.log(no);
//here is one callback value from another function
});
typeReturn(object, function(na) {
console.log(na);
//here is also a callback value from another function
});
view(example, example2);
}
function vliew(example, example2) {
console.log(example, example2);
//but here i want also to console.log the variables "no" and "na"
}
So this is my issue, is there any way to achieve what i want??
I have no idea how to make those two variables, "no" and "na" to pass them to the function "view"
Could anyone help? Thanks!
the dirty way
function(something) {
var example = 1;
var example2 = 2;
NameReturn(nameBusinessID, function(no) {
console.log(no);
//here is one callback value from another function
typeReturn(object, function(na) {
console.log(na);
//here is also a callback value from another function
view(example, example2,no, na);
});
});
}
function vliew(_example, _example2,_no,_na) {
console.log(_example)
console.log(_example2)
console.log(_no)
console.log(_na)
}
UPDATE
I have slightly modified your use case and implimented again.you can check code in this .
i am listening for button click callbacks and updating values in common object.once all four values are available,im calling the view function.
click both buttons and you will see result.
function collector() {
this.counter = 0;
this.obj = {};
this.setValues = function(name, value) {
this.counter++;
this.obj[name] = value;
if (this.counter == 4) {
view(this.obj.example, this.obj.example2, this.obj.no, this.obj.na);
}
};
}
function view(example, example1, a, b) {
$('.result').append('example :' + example + '<br>');
$('.result').append('example2 :' + example1 + '<br>');
$('.result').append('na :' + a + '<br>');
$('.result').append('no :' + b + '<br>');
}
function load() {
var example = 1;
var example2 = 2;
var valueCollector = new collector();
$('.no').on('click', function() {
var no = 3;
valueCollector.setValues('no', no);
});
$('.na').click(function() {
var na = 4;
valueCollector.setValues('na', na);
});
valueCollector.setValues('example', example);
valueCollector.setValues('example2', example2);
}
load();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="no">no</button>
<button class="na">na</button>
<div class="result"></div>

Javascript class extension

CONTEXT
I have one Base class called Entity and User and Group are both derived from this object; If I instantiate a user with the ID of 12 for example, the next time I try to do that it returns the same object. I store these in a prototype items variable. To keep these item variables separate I have to declare the User And Group functions separately although they contain the same code.
CODE
Application.prototype.Entity = function() {
};
Application.prototype.Entity.prototype.print = function() {
var container = $("<div class='user-tag'></div>");
container.append("<a class='friend_list ellipsis_overflow' style='background-image:url(\"" + this.entity.pic.icon + "\");'"
+ "href ='user?id=" + this.entity.id + "'>" + this.entity.name + "</a>");
return container;
};
//HOW TO GET RID OF THIS "REPETITION"
Application.prototype.User = function(entity) {
this.entity = entity;
this.entity.pic = this.entity.pic || Application.prototype.default.pic;
if (this.items[this.entity.id]) {
return this.items[this.entity.id];
} else {
this.items[this.entity.id] = this;
}
};
Application.prototype.Group = function(entity) {
this.entity = entity;
this.entity.pic = this.entity.pic || Application.prototype.default.pic;
if (this.items[this.entity.id]) {
return this.items[this.entity.id];
} else {
this.items[this.entity.id] = this;
}
};
// END REPEAT
Application.prototype.Group.prototype = new Application.prototype.Entity();
Application.prototype.User.prototype = new Application.prototype.Entity();
//Application.prototype.User.prototype.constructor = Application.prototype.Entity;
//Application.prototype.Group.prototype.constructor = Application.prototype.Entity; - these don't seem to work
//THESE items VARIABLES HAVE TO REMAIN SEPARATE
Application.prototype.Group.prototype.items = {};
Application.prototype.User.prototype.items = {};
QUESTION
I specifically would like to rid my code of the repetition mentioned above, but if you see any other unnecessary code, please comment. Thanks!
Something like this?
function userAndGroupConstructor(entity) {
this.entity = entity;
this.entity.pic = this.entity.pic || Application.prototype.default.pic;
if (this.items[this.entity.id]) {
return this.items[this.entity.id];
} else {
this.items[this.entity.id] = this;
}
}
Application.prototype.User = function() {
return userAndGroupConstructor.apply(this, arguments)
}
Application.prototype.Group = function() {
return userAndGroupConstructor.apply(this, arguments)
}
You get distinct constructors with distinct prototypes, but avoid duplication.
You can do this:
Application.prototype.Group = Application.prototype.User;
Since Application.prototype.User contains the function reference, you can just assign it to Application.prototype.Group.

Can not call method of undefined

I'm trying to develop a javascript object that creates a menu in html.
The function receives an object as an argument. Among the object elements is a function that should be executed in an event handler called from a method of my object.
Here is my code :
Menu = function(config) {
var j = 0;
this.config = config;
this.make = function() {
for (i = 0; i < this.config.items.length; i++) {
var vid = document.createElement("div");
vid.className = this.config.cls;
vid.id += i;
document.body.appendChild(vid);
var txt = document.createTextNode(this.config.items[i]);
var pp = document.createElement("p");
pp.appendChild(txt);
vid.appendChild(pp);
}
document.addEventListener("keydown", this.scrolldown, false);
document.onkeydown = function(e) {
var keyCode = e.keyCode;
alert("functional");
if (keyCode == 40) {
alert("You hit key down");
var et = document.getElementById(j);
this.config.trait1(et);
j = j + 1;
} else {
alert("no");
}
}
};
return this;
};
when I call the function make after instantiating the object I have my elements created but my event isn't handled because of :
Uncaught TypeError: Cannot call method 'trait1' of undefined .
Can anyone help me? I saw many answers of the same question but none of the suggested solutions worked.
this inside the Menu function is not the same as this inside the onkeydown function.
Store the value of this in another variable and use that.
Menu = function () {
var myMenu = this; // I'm assuming that you will be calling `new Menu()`
document.onkeydown = function () {
myMenu.config.etc.etc.etc
}
}

Categories