addEventListener in a loop - javascript

I'm new in this nice community. I am a young student in computer science and I'm trying to develope a web based app to help both sighted people and people with visual empairment to learn typing. My project was blocked by the following problem:
Given a string, I need to verify that the user has pressed the key that corresponds to the first character. If it is correct, the program should test the next character, until the end of the string.
The most natural solutions seems to be a loop, like that:
do {
var checkCharacter=lessons[exerciceNumber][2].substr(charNumber, 1);
document.addEventListener('keydown', testKey);
}
while (charNumber<=lessons[exerciceNumber][2].length-1);
When I run the script, nothing appears and it seems it doesn't detect the pressed keys. Can you help me please?
Inorder to understand the context, I'll attach the entire file.
<?php
include "config.php";
//Select fields from database
$sql="SELECT lesson_number, exercice_number, text FROM lessons";
$result=$conn->query($sql);
//Pass variables to javascript
$output="<script> var lessons=new Array(); ";
while ($row=$result->fetch_assoc()) {
$output.="lessons.push([
".$row["lesson_number"].",
".$row["exercice_number"].",
'".$row["text"]."']);";
}
$output.="</script>";
echo $output;
?>
<html>
<head>
<title>Impara a scrivere</title>
</head>
<body>
<h2 id="lesson"></h2>
<p id="instructions"></p>
<p id="check"></p>
<p id="keyList"></p>
</body>
<script>
//define variables
var keys="";
var exerciceNumber=0;
var charNumber=0;
//Function to play the sounds
function sound(name) {
var audio=new Audio(name+'.mp3');
audio.play();}
document.getElementById("lesson").innerHTML="Lezione "+lessons[exerciceNumber][0];
document.getElementById("instructions").innerHTML="Esercizio "+lezioni[exerciceNumber][1]+": scrivi <b>"+lessons[exrciceNumber][2]+"</b>.";
//function to check pressed keys
funtion testKey(event) {
if (event.key==checkCharacter) {
sound ("right");
keys+=event.key;
document.getElementById("keyList").innerHTML=keys;
} else {
sound("wrong");
}
}
//loop for the exercices
do {
var checkCharacter=lessons[exerciceNumber][2].substr(charNumber, 1);
document.addEventListener('keydown', testKey);
}
while (charNumber<=lessons[exerciceNumber][2].length-1);
</script>
</html>

You cannot add an event listener multiple times, that will cause it to fire once. To avoid this, try using onkeydown. For example, something like this should work:
do {
const checkCharacter = lessons[exerciceNumber][2].substr(charNumber, 1);
document.onkeydown = (evt) => {
testKey();
};
} while (charNumber <= lessons[exerciceNumber][2].length - 1);
Also, I'm not entirely sure of what you're trying to do here, but instead of using a loop you can do something like this:
form.onsubmit = (evt) => {
const input = someInput.value;
if (testKey() === input) {
return true; // Or whatever you want
}
};
Hoped this helped!

Related

Why does my Javascript Function stop working after a certain period of time?

First things first, I'm brand new to Javascript and Regex. I've only been dipping my toes in this past month. I've been trying to put together away to paste a url into a text input then automatically trim it down to just the host name and validate it before I'm able to push the button.
I've gotten it working a few different times but I keep running into the same issue: After a certain period of time, it simply stops working.
I've reformatted and cleaned up the code a few times (though, I'm sure it's still very sloppy because I'm new at this) and I can get it working again. But after an hour or so of working, it stops working. Reloading the page doesn't make a difference. Even restarting my computer doesn't make a difference. It simply stops working.
My only guess is that there must be something about the way I'm going about this which is causing it crash or stall out. Perhaps a formatting issue, perhaps the methodology altogether is flawed. I just don't know enough to be able to diagnose it yet.
Hopefully, some of you nice people would be able to point out my flaws or point me in the right direction of how to fix this. I've searched and I couldn't find anyone who was trying to do the things I'm doing all in one build (preparing to myself to be proved wrong here).
Here's the code:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Untitled Document</title>
</head>
<body>
<input id="notesUrlInput" type="text" placeholder="URL Goes here" pattern="^(?!www\.)[a-zA-Z0-9\-]+\.[a-zA-Z0-9]+$" autocomplete="off">
<button id="notesExecuteButton" disabled>Execute</button>
<span id="notesUrlOutput"></span>
<!------------------------------------------------------------------------------->
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script>
(function () {
var timeout = null;
var notesUrlOutput = document.getElementById("notesUrlOutput");
var notesExecuteButton = document.getElementById("notesExecuteButton");
document.getElementById('notesUrlInput').addEventListener('keyup',
function (e) {
clearTimeout(timeout);
timeout = setTimeout(
function () {
rawInput = $('#notesUrlInput').val();
cleanInput = rawInput.replace('www.', '');
cleanInput = cleanInput.replace('http://', '');
cleanInput = cleanInput.replace('https://', '');
cleanInput = cleanInput.replace(/\/.*/,'');
$('#notesUrlInput').val(cleanInput);
if (cleanInput.value == "") {
notesUrlOutput.innerHTML = "";
notesExecuteButton.disabled = true; return false;
} else if(!notesUrlInput.checkValidity()) {
notesUrlOutput.innerHTML = "Invalid URL: Please provide a valid URL";
notesExecuteButton.disabled = true; return false;
} else {
notesUrlOutput.innerHTML = "Input OK";
notesExecuteButton.disabled = false; return false;
}
}, 400);
});
})();
</script>
</body>
</html>
Frustratingly, when I pasted this code in here and ran it, it worked. As soon as I opened the file I copied this from in my browser. It stopped working. I just don't understand it.
From your code it looks like you want to extract just the domain name from the input field.
You mix JavaScript DOM calls and jQuery, which is fine. It is usually easier to interact with the DOM using just jQuery. Here is your code rewritten in jQuery:
const cleanRegex = /^https?:\/\/(?:www\.)?(.*)\/.*$/;
const validRegex = /^[\w\-]+(\.[\w]+)+$/;
(function () {
$('#notesExecuteButton').prop('disabled', true);
$('#notesUrlInput').on('input', function(event) {
let val = $(this).val();
let cleaned = val.replace(cleanRegex, '$1');
$(this).val(cleaned);
if(!cleaned) {
$('#notesUrlOutput').text('');
$('#notesExecuteButton').prop('disabled', true);
} else if(!cleaned.match(validRegex)) {
$('#notesUrlOutput').text('Invalid URL: Please provide a valid URL');
$('#notesExecuteButton').prop('disabled', true);
} else {
$('#notesUrlOutput').text('Input OK');
$('#notesExecuteButton').prop('disabled', false);
}
});
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
<input id="notesUrlInput" />
<button id="notesExecuteButton" style="disabled: disabled;">Go</button>
<div id="notesUrlOutput"></div>
Explanation:
.on('input') - fires every time something changes in the input field- val.replace(cleanRegex, '$1') - clean up: strip protocol and www prefix, and URL path (any text after domain
cleaned.match(validRegex) - check validity of domain
.prop('disabled', true/false) - add/remove disable property

Javascript Game: How to "suspend" a while loop to wait for a user input

I'm writing a text-based game in javascript, and one of the main "features" is a input box which accepts a user input, and submits the input by a button tag. In my main game loop a call the button's onclick:
var game_u = new game_util();
function Game_Main(){
while(active){
input = game_u.getText();
//p = new player();
active = false;
}
}
function game_util(){
this.getText = function(){
//The Submit button
confirm_plr.onclick = function(){
//Input value
return player_in.value;
}
}
}
The problem with this way, though is that the while loop does not "wait" for the submit button to be clicked to get the input from the `game_u.getText(); function and continues on with the loop.
Is there a better way for me to do this, it is my first wack at a text-based game? I don't want to use the prompt method, because it breaks the immersion of the gameplay.
I'm also coming from Java, an object-oriented programming language, so that's why I use a while loop.
Any help is appreciated.
If you want to suspend with user input, a simple prompt() box will do.
Try this:
var input = prompt("Enter data here", "");
This will wait for input and store it into variable input.
See working example on JSFiddle.net.
AFAIK, synchronous JS is not possible, as according to this SO post:
JavaScript is asynchronous, you cannot "pauses" execution. Moreover, while javascript is running the entire user interface freezes, so the user cannot click the button.
As to your question,
If I shouldn't be using a while loop, what would replace it?
because JS is event driven, and you're trying to run code every time that button is clicked (and input is entered), just use a onclick handler.
So, rather than this:
while(active) {
input = game_u.getText();
p = new player();
active = false;
}
You can do:
document.getElementById("button").addEventListener("click", function() {
input = game_u.getText();
p = new player();
active = false;
});
This will run the code every time the button is clicked, essentially the same as what you're trying to do.
One approach is to break the different stages of your game into functions corresponding to the different stages (rooms, levels etc.) which are called depending on the user's input; you will also need a variable that saves the game's current state (room in the example below).
(function Game() {
var room = 1;
document.getElementById('playerSubmit').addEventListener('click', function() {
var playerInput = document.getElementById('playerInput').value;
if (playerInput == "Go to room 2") {
room = 2;
}
if (playerInput == "Go to room 1") {
room = 1;
}
if (room == 1) {
room1(playerInput);
} else if (room == 2) {
room2(playerInput);
}
document.getElementById('playerInput').value = '';
});
function room1(playerInput) {
output('You are in the first room and entered the command ' + playerInput);
}
function room2(playerInput) {
output("Now you're in room 2. Your command was " + playerInput);
}
function output(text) {
document.getElementById('output').innerHTML += '<p>' + text + '</p>';
}
})();
#output {
border: solid 1px blue;
width: 500px;
height: 400px;
overflow: scroll;
}
label {
display: block; margin-top: 1em;
}
<div id="output"></div>
<label for="playerInput">Player input</label>
<input id="playerInput" type="text" size=50 />
<input id="playerSubmit" type="button" value="Submit" />
http://jsfiddle.net/spjs8spp/2/
You've gotten some great info in the commentary. An evented model might be ideal, but you can do persistent loops. I'm not as familiar with it, but HTML5 Canvas is just that. Maybe look into that some, since nobody else mentioned it yet.

Saving some contenteditables localstorage - - -

I'm trying to save more than one entry of contenteditable content into my localstorage for a Chrome extension. My current code saves just one contenteditable section fine, but when I try to add another Id of a seperate contenteditable section it either deletes all the saved information or doesn't do anything at all. I'm pretty novice in JS, so I hope I'm just making a simple mistake. My html looks like this:
<div id = "content">
<div id= "tcontent" contenteditable="true" data-ph=" Make a note . . . "
style= "height: 300px; overflow: auto"></div>
<div id = "content2">
<div id= "tcontent2" contenteditable="true" data-ph= " Make a note . . . "
style= "height: 300px; overflow: auto"></div>
</div>
And this is my Javascript:
window.addEventListener('load', onLoad); function onLoad() {
checkEdits();
}
function checkEdits() {
if(localStorage.userEdits!=null) {
document.getElementById("tcontent", "tcontent2").innerHTML += localStorage.userEdits;
}
};
document.onkeyup = function (e) {
e = e || window.event;
console.log(e.keyCode);
saveEdits();
};
function saveEdits() {
var editElem = document.getElementById("tcontent", "tcontent2");
var userVersion = editElem.innerHTML;
localStorage.userEdits = userVersion;
};
Basically this code will only save one (the content I place first into the getElementbyId). Isn't there a way to save both of the 'content's?
I've been playing around with all my little knowledge of javascript I have but can't seem to see what I'm doing wrong or what I should be doing here.
Much thanks for any and all help.
document.getElementById is a method that only takes one element's id. You are currently trying to pass two strings to the method. That will not work.
Please refer to the documentation here: https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById
Also, you must assign the innerHTML of each element individually to each piece of saved content in localStorage.
Granted that you are fairly new to the Language I do not want to overcomplicate the answer for you. With that said, please find below your code with a few modifications to be able to save both pieces in localStorage respectively:
window.addEventListener('load', onLoad); function onLoad() {
checkEdits();
}
function checkEdits() {
if(localStorage.userEdits1!=null) {
document.getElementById("tcontent").innerHTML = localStorage.userEdits1;
}
if(localStorage.userEdits2!=null) {
document.getElementById("tcontent2").innerHTML = localStorage.userEdits2;
}
};
document.onkeyup = function (e) {
e = e || window.event;
console.log(e.keyCode);
saveEdits();
};
function saveEdits() {
var editElem1 = document.getElementById("tcontent");
var editElem2 = document.getElementById("tcontent2");
localStorage.userEdits1 = editElem1.innerHTML;
localStorage.userEdits2 = editElem2.innerHTML;
};

How to use event listener in JS

Im trying to use the same buttons for different function in the program. So I tried using event listeners but for some reason its not working. Could look at the code and tell me what I have done wrong? Thanks. (I omited the HTML tags and so for shortening the posted code)
<script type="text/javascript">
var photo = "photo";
var edgar = "edgar"
var x = document.getElementById("yes");
x.addEventListener("click", choiceyesstory);
x.addEventListener("click", choiceyesfriendly);
var y = document.getElementByID("no");
y.addEventListener("Click", choicenostory);
y.addEventListener("click", choicenofriendly);
function choiceyesstory(x) {
document.getElementById("photo").src = "images/dragon1.jpg";
document.getElementById("stage1").innerHTML = "Once upon a time";
setTimeout("choice2()",5*1000);
}
function choicenostory(y) {
document.getElementById("photo").src = "images/sea.jpg";
document.getElementById("stage1").innerHTML = "Would you like to
listen to some music?";
document.getElementById("edgar").play();
}
function choice2(x){
document.getElementById("photo").src = "images/dragon9.jpg";
document.getElementById("stage1").innerHTML = "Was he a friendly
dragon?";
}
function choiceyesfriendly(x) {
{document.getElementById("photo").src = "images/dragon2.jpg";
document.getElementById("stage1").innerHTML = "He had many friends";
}
function choicenofriendly(y)
{ document.getElementByID ("photo").src = "images/dragon3.jpg";
document.getElementById("stage1").innerHTML = "He did so and so";
}
</script>
<body>
<button id="yes">Yes</button>
<button id="no">No</button>
</body>
Edit / Delete Edit Post Quick reply to this message Reply Reply With Quote Reply With Quote Multi-Quote This Message
Reply to Thread
Quick Navigation DOM and JSON scripting Top
Quick Reply Quick Reply
I took yours js and simplified it. The event listener by itself is working ok. Maybe you have issue somewhere else.
http://jsfiddle.net/jnmav1wk/
var x = document.getElementById("yes");
console.log(x);
x.addEventListener("click", choiceyesstory);
function choiceyesstory(x) {
alert('Clicked');
}
Try to simplify your problem, get rid of the NO and all corresponding code, keep removing code till it's working. Then add feature by feature till it will stop working and you know where it's broken.
I use console.log(); and alert(); to make it more verbose to me.
Sometimes the code needs to be inside this:
$( document ).ready(function() {
yourCode();
});
This needs jQuery, if you want avoid it, here are some alternatives:
$(document).ready equivalent without jQuery
Because if the functions are executed against HTML code which is in middle of loading (and maybe the YES button is not there yet while you try to attach the event listener to it, etc...)

display message javascript while a calculation is being made

I have been looking around and I cannot seem to figure out how to do this, although it seems like it would be very simple.(mobile development)
What I am trying to do is display a message (kind of like an alert, but not an alert, more like a dialog) while a calculation is being made. Simply like a Loading please wait. I want the message to appear and stay there while the calculation is being done and then be removed. I just cannot seem to find a proper way of doing this.
The submit button is pressed and first checks to make sure all the forms are filled out then it should show the message, it does the calculation, then hides the message.
Here is the Calculation function.
function scpdResults(form) {
//call all of the "choice" functions here
//otherwise, when the page is refreshed, the pulldown might not match the variable
//this shouldn't be a problem, but this is the defensive way to code it
choiceVoltage(form);
choiceMotorRatingVal(form);
getMotorRatingType();
getProduct();
getConnection();
getDisconnect();
getDisclaimer();
getMotorType();
//restore these fields to their default values every time submit is clicked
//this puts the results table into a known state
//it is also used in error checking in the populateResults function
document.getElementById('results').innerHTML = "Results:";
document.getElementById('fuse_cb_sel').innerHTML = "Fuse/CB 1:";
document.getElementById('fuse_cb_sel_2').innerHTML = "Fuse/CB 2:";
document.getElementById('fuse_cb_result').innerHTML = "(result1)";
document.getElementById('fuse_cb_res_2').innerHTML = "(result2)";
document.getElementById('sccr_2').innerHTML = "<b>Fault Rating:</b>";
document.getElementById('sccr_result').innerHTML = "(result)";
document.getElementById('sccr_result_2').innerHTML = "(result)";
document.getElementById('contactor_result').innerHTML = "(result)";
document.getElementById('controller_result').innerHTML = "(result)";
//Make sure something has been selected for each variable
if (product === "Choose an Option." || product === "") {
alert("You must select a value for every field. Select a Value for Product");
**************BLAH************
} else {
//valid entries, so jump to results table
document.location.href = '#results_a';
******This is where the message should start being displayed***********
document.getElementById('motor_result').innerHTML = motorRatingVal + " " + motorRatingType;
document.getElementById('voltage_res_2').innerHTML = voltage + " V";
document.getElementById('product_res_2').innerHTML = product;
document.getElementById('connection_res_2').innerHTML = connection;
document.getElementById('disconnect_res_2').innerHTML = disconnect;
if (BLAH) {
}
else {
}
populateResults();
document.getElementById('CalculatedResults').style.display = "block";
} //end massive else statement that ensures all fields have values
*****Close out of the Loading message********
} //scpd results
Thank you all for your time, it is greatly appreciated
It is a good idea to separate your display code from the calculation code. It should roughly look like this
displayDialog();
makeCalculation();
closeDialog();
If you are having trouble with any of those steps, please add it to your question.
Computers are fast. Really fast. Most modern computers can do several billion instructions per second. Therefore, I'm fairly certain you can rely on a a setTimeout function to fire around 1000ms to be sufficient to show a loading message.
if (product === "Choose an Option." || product === "") {
/* ... */
} else {
/* ... */
var loader = document.getElementById('loader');
loader.style.display = 'block';
window.setTimeout(function() {
loader.style.display = 'none';
document.getElementById('CalculatedResults').style.display = "block";
}, 1000);
}
<div id="loader" style="display: none;">Please wait while we calculate.</div>
You need to give the UI main thread a chance to render your message before starting your calculation.
This is often done like this:
showMessage();
setTimeout(function() {
doCalculation();
cleanUp()
}, 0);
Using the timer allows the code to fall through into the event loop, update the UI, and then start up the calculation.
You're already using a section to pop up a "results" page -- why not pop up a "calculating" page?
Really, there are 4,000,000 different ways of tackling this problem, but why not try writing a "displayCalculatingMessage" function and a "removeCalculatingMessage" function, if you don't want to get all object-oriented on such a simple thing.
function displayCalculatingMessage () {
var submit_button = getSubmitButton();
submit_button.disabled = true;
// optionally get all inputs and disable those, as well
// now, you can either do something like pop up another hidden div,
// that has the loading message in it...
// or you could do something like:
var loading_span = document.createElement("span");
loading_span.id = "loading-message";
loading_span.innerText = "working...";
submit_button.parentElement.replaceChild(loading_span, submit_button);
}
function removeCalculatingMessage () {
var submit_button = getSubmitButton(),
loading_span = document.getElementById("loading-message");
submit_button.disabled = false;
loading_span.parentElement.replaceChild(submit_button, loading_span);
// and then reenable any other disabled elements, et cetera.
// then bring up your results div...
// ...or bring up your results div and do this after
}
There are a billion ways of accomplishing this, it all comes down to how you want it to appear to the user -- WHAT you want to have happen.

Categories