Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I am trying retrieve a value from one function and would like to use that value in another function. This function returns a city, state and I present that on screen.
<script>
$.get("http://ipinfo.io", function (response) {
$("#address").html(response.city + ", " + response.region);
}, "jsonp");
</script>
<div id="address"></div>
But I would like to use the city value for another piece of JS that uses city values to return current weather. That code is here:
$(document).ready(function() {
loadWeather('Baltimore',''); //#params location, woeid
});
'Baltimore' is currently hard-coded in the JS and I would like the returned city value from above to be placed in the weather function. The weather function only seems to accept the city as a string and not a code snippet like the answers below have presented. Can I make the resulting city into a variable that is then passed to the weather function?
You could consider calling your loadWeather() function within the callback function of your get() AJAX call :
<script>
$(function(){
// Get your city information
$.get("http://ipinfo.io", function (response) {
// At this point you have a response, so use it
$("#address").html(response.city + ", " + response.region);
// Pass the city to your loadWeather function
loadWeather(response.city,'');
}, "jsonp");
});
</script>
The order in which you call these functions is important as your loadWeather() function clearly depends on you retreiving a value from your get() call (as you actually need the city to load the weather for).
You can't use a value before you have that value. Call the other function when you have the value you need.
$(document).ready(function() {
$.get("http://ipinfo.io", function (response) {
$("#address").html(response.city + ", " + response.region);
loadWeather(response.city,''); //#params location, woeid
}, "jsonp");
});
Calling loadWeather within your jQuery.get() callback will give loadWeather access to the data in response, but doesn't really answer your original question: how do you use info from one function in another? The simplest solution would be to pull things apart into separate functions, so you could use the output of one function as an argument of the other:
loadWeather(getLocationFromIp());
That won't work here, though, since the operation to get location data is asynchronous -- there's no way to be sure when you'll get the data you need from the server. That's why you used a callback function in the first place.
Sharing Asynchronous Data with Promises
You can get around this, while still keeping the weather update and location update functions separated, by using jQuery's deferred/promise features. This also gives you a chance to handle the location request to ipinfo.io failing.
And while we're at it, let's handle cases (like mine) where ipinfo.io returns '' for city and region. In that case, falling back to latitude & longitude will still give the simpleWeather plugin what it needs.
$(document).ready(function() {
getLocationFromIp()
.done(function(response) {
var has_city = (response.city && response.region);
var location = has_city ? (response.city + ', ' + response.region) : response.loc;
$('#address').html(location);
loadWeather(location);
})
.fail(function() {
alert('Error getting location info. Try reloading in a moment.');
});
});
function getLocationFromIp() {
return $.getJSON('http://ipinfo.io');
}
Okay, but what's the point of using promises and a separate getLocationFromIp function? Except for the failure message (which simpleWeather could handle better than alert()), you could do all that in the original $.get() callback.
Reusing Data with Caching
The benefit of breaking things into smaller, independent pieces appears when you move a little beyond what your pen is currently doing. Now you can use other methods of passing one function's data to another to do things like update the weather without having to re-request location data from ipinfo.io.
I'm guessing, from the location search field and use of Google Maps autocomplete, that you have more in mind for this code. So let's go ahead and expand things a little. For an example, we'll cache (store) the user's detected location with jQuery.data() then refresh the weather every 10 minutes using that information:
$(document).ready(function() {
getLocationFromIp()
.done(function(response) {
var has_city = (response.city && response.region);
var location = has_city ? (response.city + ', ' + response.region) : response.loc;
$('#address')
.html(location)
.data('location', location);
loadWeather(location);
// update every 10 min
setInterval(loadWeather, 60000);
})
.fail(function() {
// Updating simpleWeather with an invalid location triggers the plugin's error message:
loadWeather('0');
});
});
function getLocationFromIp() {
return $.getJSON('http://ipinfo.io');
}
function loadWeather(location) {
// if no location argument passed, use cached location
var loc = location || $('#address').data('location');
$.simpleWeather({
location: loc,
// more simpleWeather config...
});
}
You can see the above approach in full here: http://codepen.io/adammessinger/pen/VaOJdq
Sharing Data Among Functions with Encapsulation
If you wanted to get a bit fancier, you could also cache and reuse the location info without touching the DOM by leveraging the Revealing Module Pattern and closure.
All the functions in that module would have access to the current location data. You could expose some or all of them as public methods for manipulating the state of your weather widget. For example, updating the weather info to match whatever location the user types into input#searchTextField with WeatherWidget.updateLocation(input.value) or turning timed refresh of the weather data on and off when the user toggles a checkbox.
This bundling of data together with relevant functions is a form of encapsulation, a facet of object-oriented programming. But I'm getting far from the original question, so I'll leave it at that.
Related
I am trying to store the value of a checkbox in the popup.html file. I have these two functions in my popup.js file:
function storeUserPrefs() {
var isHighlighting = false;
var highlightedCheckBoxVal = $("#highlightedCheckbox").is(":checked");
chrome.storage.sync.set({isHighlighting: highlightedCheckBoxVal}, function() {
console.log("saved " + isHighlighting + "as " + highlightedCheckBoxVal);
})
}
function getUserPrefs() {
chrome.storage.sync.get(['isHighlighting'], function(result) {
console.log('Value is currently ' + result.isHighlighting);
});
}
My first question is:
If I am trying to save the true/false value of the variable isHighlighting and then set the saved value to the highlightedCheckBoxVal will these two functions do that correctly?
My second question is:
Where do I call these two functions? Should I keep them in popup.js or should I put one in background.js?
My third question is:
Would I use the Chrome.storage.onChanged function to update these values every time the checkbox is checked/unchecked?
Any help is greatly appreciated, and please let me know if you need any more details.
trying to save the true/false
set() is fine.
get() does nothing useful so its callback needs something like
$("#highlightedCheckbox").prop('checked', result.isHighlighting)
Where do I call these two functions
In the popup.
The background script runs in a separate hidden background page with its own empty DOM.
use the Chrome.storage.onChanged function
No.
Register a change or click listener that calls set() to store the new value.
Do it at the beginning of the script and add getUserPrefs() call to load the data every time the popup opens:
$("#highlightedCheckbox").change(storeUserPrefs)
getUserPrefs();
On my web app, the user is asked a question and can choose only one of two answers. Yes or no. A query string is created based on their answer.
The following code carries the query string through the URL of every page:
var navlinks = document.getElementsByClassName("qString");
$(navlinks).prop("href", function() { return this.href + location.search; })
There are only 2 query strings, ?choice=yes and ?choice=no.
Once the user is taken through the app, if they navigate to either park01.html, park02.html, or park03.html from any other page, data will be pulled accordingly via a called function().
Here's my concept in pseudocode:
// I assume I should store specific html pages to a variable
var parkPages = ["park01.html", "park02.html", "park03.html”];
if (user clicks on specified html pages stored in variable) {
and the url contains = ?choice=yes;
Then call these functions: funcA(), funcB(), funcC();
}
else {
the url contains = ?choice=no;
Then call these functions: funcD(), funcE(), funcF();
}
Does the concept make sense? And what does the syntax look like?
If you're simply looking for a concrete translation of your pseudocode into JavaScript, based on your last comment, this should be what you need:
if (location.search === "?choice=yes") {
funcA();
funcB();
funcC();
}
else {
funcD();
funcE();
funcF();
}
Though at this stage, I'd recommend spending less time here and more on instructional/tutorial based websites.
Using jQuery I'm writing a website api call in Javascript, which so far works pretty well. When a person updates a number in a text input it does a call to the API and updates a field with the response. It gets problematic however, when I user quickly makes a lot of changes. The javascript then seems to pile up all queries, and somehow does them side by side, which gives the field to be updated kind of a stressy look.
I think one way of giving the user a more relaxed interface, is to only start the API call after the user finished editing the input field for more than half a second ago. I can of course set a timeout, but after the timeout I need to check if there is not already a call under way. If there is, it would need to be stopped/killed/disregarded, and then simply start the new call.
First of all, does this seem like a logical way of doing it? Next, how do I check if a call is underway? And lastly, how do I stop/kill/disregard the call that is busy?
All tips are welcome!
[EDIT]
As requested, here some of the code I already have:
function updateSellAmount() {
$("#sellAmount").addClass('loadgif');
fieldToBeUpdated = 'sellAmount';
var buyAmount = $("#buyAmount").val();
var sellCurrency = $("#sellCurrency").val();
var buyCurrency = $("#buyCurrency").val();
var quoteURL = "/api/getQuote/?sellCurrency="+sellCurrency
+"&buyAmount="+buyAmount
+"&buyCurrency="+buyCurrency;
$.get(quoteURL, function(data, textStatus, jqXHR){
if (textStatus == "success") {
$("#sellAmount").val(data);
$("#sellAmount").removeClass('loadgif');
}
});
if (fieldToBeUpdated == 'sellAmount') {
setTimeout(updatesellAmount, 10000);
}
}
$("#buyAmount").on("change keyup paste", function(){
updateSellAmount();
});
If you make your AJAX call like this:
var myAjaxDeferred = $.ajax("....");
You can check it later with:
if (myAjaxDeferred.state() === "pending") {
// this call is still working...
}
I have an object containing share counts for various social metrics. The object looks like this:
Object{
delicious: 0,
facebook: {
comments: 0,
likes: 0,
shares: 0,
total: 0,
},
linkedIn: 1,
pinterest: 0,
twitter: 9
}
Here is some testing code I am using to try to access my object:
console.log(bg.results);
console.log(bg.results["facebook"]);
console.log(bg.results.facebook);
Object.keys(bg.results).forEach(function(key){
console.log(bg.results);
console.log(key + "->" + bg.results[key]);
});
The object above is what I am seeing in the console, so I know that the fields in bg.results contain data. The problem is, when I try to access any of these fields using either dot syntax or by using object["key"] I get an empty string as the result. I searched and could not find anyone else experiencing the same problem. Does anyone have any ideas why this may be happening?
Some additional info:
My code is being run within the context of a chrome extension. I'm accessing this object from a popup page, and the object's data is being supplied from my background page.
Thank you for your assistance.
UPDATE
Something funny is going on with how Chrome is handling my code. The data in bg.results is supplied by a function on my background page called update. In my program there are two ways that update is called depending on the user's settings. When update() is called from within the context of my background page, everything works fine and behaves as expected. When update() is called from my popup page, I get empty strings when I try to access any fields.
This seems like a bug very particular to how chrome extensions are handled, so any input from experts in this sort of thing would be awesome. Thanks for the help everyone!
1.) your code should work. facebook is an object itself so you need to think about that, and maybe use a recursive function!
2.) why dont you just loop over your objects properties with the for-in-loop?
for(key in bg.results) {
console.log(key);
}
3.) example recursive function
function recursiveExample(obj) {
var current;
for(key in obj) {
current = obj[key];
if(typeof current === 'object' && !Array.isArray(current)) {
recursiveExample(current);
}
console.log("key: " + key + " - value" + current);
}
}
I've concluded that my values were not completely loaded into the results object when I was trying to access them. I can only speculate as to why I could see the values when I printed the results object to the console (and could not access them at that point) but was not able to access them until later. Below is the solution I used for this problem.
function checkLoaded(results){
var isLoaded = true;
$.each(results, function(key, value){
if(value === "" || value === undefined){
isLoaded = false;
}
});
setTimeout(function(){
if(!isLoaded){
checkLoaded(results);
}
else{
displayHandler(results);
}
}, 500);
}
If anyone knows why I could see the values in the console but could not access them, I'd appreciate an explanation. But otherwise, if anyone else encounters a problem like this, try setting up a delay to make sure that your values are completely loaded into your collection.
This is just a guess (since you haven't posted any of the relevant data-loading code), but I suspect you may be doing something like this:
chrome.extension.sendMessage({myAction: "update"}, function(response) {
bg.results = response;
});
// we try to use bg.results, but the sendMessage callback won't be run until later on
Object.keys(bg.results).forEach(function(key){
console.log(bg.results);
console.log(key + "->" + bg.results[key]);
});
The ansychronous callback passed to sendMessage runs after the current function stack has cleared (i.e., once the JavaScript thread has run out of other things to do), so bg.results = response won't happen until after you try to use it. Instead, use the results inside the callback:
chrome.extension.sendMessage({myAction: "update"}, function(response) {
bg.results = response;
// now it works, because this code is inside the callback
Object.keys(bg.results).forEach(function(key){
console.log(bg.results);
console.log(key + "->" + bg.results[key]);
});
});
Maybe you don't actually use chrome.extension.sendMessage, but you must be using some asynchronous operation (maybe Ajax, maybe another Chrome API call). I don't know what that method is for certain, since you didn't post any of your data-loading code.
Very confused here.
I have a search box which reads a list of school names from my database. When I select a school, the id (from the db) gets put in a hidden textbox.
I also have a search box which reads a list of courses from my database. However, I made the query so that it only reads the courses from the selected school.
It does that, in theory.
I was planning to pass the school id, which I grab from the hidden box, to the search script which in turn passes it to my database query. However, the variable I put my school id in doesn't seem to be updating.. yet it does. Let me explain.
I come on the page. The school for my test account has id 1. The id number in my hidden box is indeed 1. I search for a school which I know has some courses assigned to it: the id number in the box changes to 3.
I have a JS variable called school_id which I declared outside of my $(document).ready. I assume that means it's global (that's what I got taught even though SO told me once it isn't really the correct way to do this. Still have to look into that). I wrote a function which updates this variable when the school search box loses focus:
$("#school").blur(function() {
school_id = $("#school_id").val();
});
A quick javascript:alert(school_id); in my browser bar also shows the updated variable: it is now 3 instead of 1.
Onto the search script part of my page (excerpt of the script):
script:"/profiel/search_richting?json=true&limit=6&id=" + school_id + "&"
As you can see, I pass the school_id variable to the script here. However, what seems to be happening is that it always passes '1', the default variable when the page loads. It simply ignores the updated variable. Does this string get parsed when the page loads? In other words, as soon as the page loads, does it actually say &id=1? That's the only idea I can come up with why it would always pass '1'.
Is there a way to make this variable update in my script string? Or what would be the best way to solve this? I'm probably missing out on something very simple here again, as usual. Thanks a lot.
EDIT
Updated per request. I added a function getTheString as was suggest and I use the value of this function to get the URL. Still doesn't work though, it still seems to be concatenating before I get a chance to update the var. HOWEVER, with this code, my ajax log says id:[object HTMLInputElement], instead of id:1. Not sure what that means.
<script type="text/javascript">
var school_id;
$(document).ready(function() {
$("#school").blur(function() {
school_id = $("#school_id").val();
});
// zoekfunctie
var scholen = {
script:"/profiel/search_school?json=true&limit=6&",
varname:"input",
json:true,
shownoresults:false,
maxresults:6,
callback: function (obj) { document.getElementById('school_id').value = obj.id; }
};
var as_json = new bsn.AutoSuggest('school', scholen);
var richtingen = {
script: getTheString(),
varname:"input",
json:true,
shownoresults:true,
maxresults:6
};
var as_json2 = new bsn.AutoSuggest('studierichting', richtingen);
});
function getTheString() {
return "/profiel/search_richting?json=true&limit=6&id=" + school_id + "&";
}
</script>
This is because the URL is static, it is not updated as the ID changes.
You should update the URL as part of the code you wrote to get the ID:
$("#school").blur(function() {
school_id = $("#school_id").val();
// update URL here ...
});
Aren't you concatenating script:"/profiel/search_richting?json=true&limit=6&id=" + school_id + "&" before the event is fired and the var updated?
Okay. So the problem was my third party plug-in instead of the code I wrote. I fixed this by editing the code of the autoSuggest plugin so it now includes my id field in the AJAX request.
var url = this.oP.script+this.oP.varname+"="+encodeURIComponent(this.sInp)+"&id="+ $("#school_id").val();
Thanks to everyone who tried to help me out!