I'm developing a weather app, and I only want the code to execute once the Enter key is pressed or when a user clicks on Submit. The problem is, the code executes whenever any key is pressed, and I'm not sure why? It wouldn't ordinarily be a big deal, but it's requesting the API every time and I only get 60 requests a minute, so two or three searches in that time will reach that limit.
let button = document.querySelector("#button");
let searchBox = document.querySelector("#search-box");
let city = document.querySelector(".city");
let feelsLike = document.querySelector(".feels-like");
let temperature = document.querySelector(".temp");
let weatherDescription = document.querySelector(".weather");
let windSpeed = document.querySelector(".wind");
let icons = document.querySelector(".icons");
searchBox.addEventListener("keypress", function (event) {
if (event.key === "Enter") {
document.getElementById("button").click();
}
fetch(
"https://api.openweathermap.org/data/2.5/weather?q=" +
searchBox.value +
"&units=metric&appid="
)
...Rest of code to be executed
)};
I think it would make sense for the enter key to emulate a button press, but I'm not entirely sure how to do that - and any resources I've used online haven't helped, unfortunately.
In your listener searchBox.addEventListener("keypress", function (event) {..} you're executing the fetch function everytime when a keypress occurs. Since the if-condition does not enclosure the your fetch execution.
Try this:
if (event.key === "Enter") {
document.getElementById("button").click();
fetch(
"https://api.openweathermap.org/data/2.5/weather?q=" +
searchBox.value +
"&units=metric&appid="
)
}
You should put the (fetch) logic in the click handler for the button.
Some other points:
If you need code to execute after you get the response from your fetch, then put that code in a chained then callback. Note that the promise that fetch returns, resolves to a response object, and you'll need to call one of its methods to get yet another promise back, which in turn will resolve to the actual data.
Don't call .click(). It is better to put the targeted code in a named function, and then call that function both here, and in the button's click handler.
You already had a variable for the button, so no need to call again getElemebtById.
function process() {
fetch(
"https://api.openweathermap.org/data/2.5/weather?q=" +
searchBox.value +
"&units=metric&appid="
).then(function (response) {
return response.json(); // <-- maybe? or .text() ?
}).then(function (data) {
//...Rest of code to be executed
});
});
button.addEventListener("click", process);
searchBox.addEventListener("keypress", function (event) {
if (event.key === "Enter") process();
)};
As stated by Amacado, the fetch is outside the if.
Moreover, if the search box is actually an HTML <input type="search"> element, there is already an OnSearch event handler that takes account of both the enter key and the magnifying glass icon in the phone keyboard (search event).
Related
I have a button user can click on that makes an http call to fetch data.
When user click the button the first time, I make http call. If the user clicks the button again, just provide the data that was fetched earlier.
How do I do that without storing it in a local variable explicitly? I have tried few things but none seem to work. It always makes the http request. I have tried to use "shareReplay" and "share" operators.
<button (click)=getData()>Click me</button>
getData() {
source$.pipe(
switchMap(sourceVal => {
const source2$ = this.getSource2();
const source3$ = this.getSource3(); -------- I do not want this call to be made on subsequent button clicks because it's a reference data
return combineLatest([source2$, source3$])
}),
map(([source2Val, source3Val]) => {
//do some processing
return 'done'
})
)
}
I am using angular and rxjs.
you can disable the button or prevent sending multiple requests via a variable.
fetching = false;
getData() {
if(!this.fetching) {
this.fetching = true;
this.http.get('url').pipe(shareReplay(1), finalize(() => {
this.fetching = false;
}));
}
}
I am creating a simple tictactoe terminal game in JS. I use the variable named player1Input to get user prompt. If the prompt is not equal to "X", I call the function again to make sure the user input the right input. If I enter the wrong input multiple times, the function (player1Game) ends up being called multiple times instead of once. How do I get it to just be called once. I put a snippet of my code at the bottom.I commented the part of the code that makes the function run twice
function player1Game () {
let player1Input = prompt(`${player1Name.charAt(0).toUpperCase() + player1Name.slice(1) } please enter \"x\": `);
//Create an error that responds if player 1 does not type x
if (player1Input !== "x") {
console.log("Please make sure you type in x")
player1Game();
//the function runs multiple times here instead of once.
// How do I get it to run only once.
}
You still aren't showing the whole context here, but perhaps you just need to return after you call it again so that the rest of the function doesn't execute when the function doesn't meet the input requirements:
function player1Game () {
let player1Input = prompt(`${player1Name.charAt(0).toUpperCase() + player1Name.slice(1) } please enter \"x\": `);
//Create an error that responds if player 1 does not type x
if (player1Input !== "x") {
console.log("Please make sure you type in x")
player1Game();
// return so it doesn't execute any more of the function
return;
}
// code here will only get called if the `player1Input`
// meets the above critera
// Rest of your code here...
}
Or, you could use an if/else:
function player1Game () {
let player1Input = prompt(`${player1Name.charAt(0).toUpperCase() + player1Name.slice(1) } please enter \"x\": `);
//Create an error that responds if player 1 does not type x
if (player1Input !== "x") {
console.log("Please make sure you type in x")
player1Game();
} else {
// code here will only get called if the `player1Input`
// meets the above critera
// Rest of your code here...
}
}
FYI, there's nothing special here. This is just normal function control flow in Javascript. If you want no more of the function to execution then insert a return statement. Or protect branches of code with an if/else so a condition will control what code executes.
The task is to fetch some data from pokemon api and append it to the list. Api request results have links on previous and next pages.
HTML:
<ul class="poke-list"></ul>
<div class="pagination">
<button class="prev">Prev</button>
<button class="next">Next</button>
</div>
here's a function that makes an api call (async await is necessary):
var getData = async function (url, pokemonName) {
var response;
if(!pokemonName) {
response = await $.get(url);
} else {
response = await $.get(url + pokemonName);
}
return response;
};
A function to append content and handle click events:
var appendContent = function (data) {
var list = $('.poke-list');
list.empty();
var res = data;
var pokemons = res.results;
pokemons.forEach(function (item) {
list.append($(`<li>${item.name}</li>`));
$('.prev').on('click', function (event) {
res = data;
var url2 = res.previous;
if (url2 === undefined || url2 === null) {
alert('Limit reached!');
} else {
getData(url2)
.then(appendContent);
}
});
$('.next').on('click', function (event) {
res = data;
var url = res.next;
if (url === undefined || url ===null) {
alert('Limit reached!');
} else {
getData(url)
.then(appendContent)
});
});
I call it on page load (yes it is necessary):
$(function {
getData()
.then(appendcontent)
here is a fiddle: https://jsfiddle.net/aikast/4rgcvd7z/
What happens is that every time append function is called it creates new click events (I know that's how it should work), and the code stops working properly (it does not see current ajax call results, so every value is null).
Stopping event propagation did not help.
Is there a way to organise it differently? I can't find a way for click event handlers to see ajax call results outside of appendContent function scope
The question was marked duplicate, but buttons are static, they are not dynamically added!
you should try something like below.
now only once click event fired.
$(next).unbind( "click" );
so every time when your function call, first it will unbind previous event.
you can check live demo as well.
https://jsfiddle.net/zhna7ksu/
.unbind() is deprecated and you should use the .off() method instead. Simply call .off() right before you call .on()
$(next).off().on("click", .......);
store data/res (why double naming the same variable?) somewhere outside appendData (window.pokemonResponse for example) function context and declare previous and next logic outside appendData. Also, use let instead of var, even IE 11 supports let
I am developing my app, and one of the features will be messaging within the application. What I did, is I've developed 'send message' window, where user can send message to other user. The logic behind it is as following:
1. User A sends message to User B.
2. Firebase creates following nodes in 'Messaging':
"Messaging"->"User A"->"User B"->"Date & Time"->"UserA: Message"
"Messaging"->"User B"->"User A"->"Date & Time"->"UserA: Message"
Here is the code that I am using for sending messages:
sendMsg: function(receiver, content) {
var user = Auth.getUser();
var sender = user.facebook.id;
var receiverId = receiver;
var receiverRef = $firebase(XXX.firebase.child("Messaging").child(receiverId).child(sender).child(Date()));
var senderRef = $firebase(XXX.firebase.child("Messaging").child(sender).child(receiverId).child(Date()));
receiverRef.$set(sender,content);
senderRef.$set(sender,content);
},
(picture 1 in imgur album)
At the moment, I am trying to read the messages from the database, and sort them in according to date. What I've accomplished so far, is that I have stored the content of "Messaging/UserA/" in form of an Object. The object could be seen in the picture I've attached (picture 2).
http://imgur.com/a/3zQ0o
Code for data receiving:
getMsgs: function () {
var user = Auth.getUser();
var userId = user.facebook.id;
var messagesPath = new Firebase("https://xxx.firebaseio.com/Messaging/");
var Messages = messagesPath.child(userId);
Messages.on("value", function (snapshot) {
var messagesObj = snapshot.val();
return messagesObj;
}, function (errorObject) {
console.log("Error code: " + errorObject.code);
});
}
My question is: how can I read the object's messages? I would like to sort the according to the date, get the message and get the Id of user who has sent the message.
Thank you so much!
You seem to be falling for the asynchronous loading trap when you're reading the messages:
getMsgs: function () {
var user = Auth.getUser();
var userId = user.facebook.id;
var messagesPath = new Firebase("https://xxx.firebaseio.com/Messaging/");
var Messages = messagesPath.child(userId);
Messages.on("value", function (snapshot) {
var messagesObj = snapshot.val();
return messagesObj;
}, function (errorObject) {
console.log("Error code: " + errorObject.code);
});
}
That return statement that you have in the Messages.on("value" callback doesn't return that value to anyone.
It's often a bit easier to see what is going on, if we split the callback off into a separate function:
onMessagesChanged(snapshot) {
// when we get here, either the messages have initially loaded
// OR there has been a change in the messages
console.log('Inside on-value listener');
var messagesObj = snapshot.val();
return messagesObj;
},
getMsgs: function () {
var user = Auth.getUser();
var userId = user.facebook.id;
var messagesPath = new Firebase("https://xxx.firebaseio.com/Messaging/");
var Messages = messagesPath.child(userId);
console.log('Before adding on-value listener');
Messages.on("value", onMessagesChanged);
console.log('After adding on-value listener');
}
If you run the snippet like this, you will see that the console logs:
Before adding on-value listener
After adding on-value listener
Inside on-value listener
This is probably not what you expected and is caused by the fact that Firebase has to retrieve the messages from its servers, which could potentially take a long time. Instead of making the user wait, the browser continues executing the code and calls your so-called callback function whenever the data is available.
In the case of Firebase your function may actually be called many times, whenever a users changes or adds a message. So the output more likely will be:
Before adding on-value listener
After adding on-value listener
Inside on-value listener
Inside on-value listener
Inside on-value listener
...
Because the callback function is triggered asynchronously, you cannot return a value to the original function from it. The simplest way to work around this problem is to perform the update of your screens inside the callback. So say you want to log the messages, you'd do:
onMessagesChanged(snapshot) {
// when we get here, either the messages have initially loaded
// OR there has been a change in the messages
console.log('Inside on-value listener');
var i = 0;
snapshot.forEach(function(messageSnapshot) {
console.log((i++)+': '+messageSnapshot.val());
});
},
Note that this problem is the same no matter what API you use to access Firebase. But the different libraries handle it in different ways. For example: AngularFire shields you from a lot of these complexities, by notifying AngularJS of the data changes for you when it gets back.
Also see: Asynchronous access to an array in Firebase
I'm working on a text-based browser game made using Javascript and JQuery.
I've run into a problem with user input.
What I used to do was just use a prompt() because it was easy to use, allowed me to save the input to a variable and made the code wait for input.
Now I want it to look better, so I've made my own input field with a nice chatbox above it.
What I wanted to know was how to stop the code from executing untill the user has pressed enter (I already detect the enter press and the text is being appended to the dialog box, I just need the code to wait for that to happen before it continues).
Here's some of my code:
Function for input
function input_field() {
var inputField = $('#dialog-input');
var messageField = $('#dialog-text');
inputField.keyup(function (e) {
if (e.keyCode == 13) {
window.userInput = inputField.val();
if(userInput != '') {
messageField.append('You: ' + window.userInput + '<br>');
inputField.val('');
messageField.scrollTop(999999999);
}
}
});
}
What I'd like to do is just save the data to a variable and then check it with a switch-case just like with prompt(), but as it stands now it just gives me an error because it tries to execute all the code instead of wait untill the user has given input.
It is possible with asynch+await.
Please note, that it is currently not fully supported by all browsers (http://caniuse.com/#search=await) and that there are probably better solutions to this kind of problems.
Using await myAsyncFunction() makes the browser treat the function like it is synchrounous, therefore it stops further execution of this function until a result is returned by the awaited function. It does not block other code on the website from running.
await may only be used inside a async function, otherwise the browser will throw an error.
Here is an example showing how you could wait for user input with await+async:
async function handleForm() {
let userInput = '';
console.log('Before getting the user input: ', userInput);
userInput = await getUserInput();
console.log('After getting user input: ', userInput);
};
function getUserInput() {
return new Promise((resolve, reject) => {
$('#myInput').keydown(function(e) {
if (e.keyCode == 13) {
const inputVal = $(this).val();
resolve(inputVal);
}
});
});
};
handleForm();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="myInput">
You have to thing differently, while javascript stop and wait for a returned value when using prompt(), you can't directly achieve that with a custom prompt.
var result = [];
for(var i = 0; i > 5; i--){
result.push( prompt('tip something') ); // Ok
};
javascript will break until prompt return something
but you can't tell your code to stop, like the browser does when promprting. You'll have to execute a code when closing the modal (on button click or keypress enter) that open/update the next modal.