Chrome extension global variable weirdness - javascript

I have the following code in the popup.js of a chrome extension.
var tt;
chrome.tabs.query({ active: true, currentWindow: true, windowType: 'normal' },
function (tabs) {
tt = 5;
});
document.getElementById('elm').textContent = tt;
But the weird thing is value of tt is undefined when accessed out side of the function, but it shows "5" if the document.getElementById('elm').textContent = tt; is put inside the function. So why is the variable value is not retained when the control exit the function ?
What am I doing wrong here ?
This code runs when the popup is shown. I.e. when the browser action's button is clicked and I simplified the code be readable. Actually I'm trying to get the current tab's id in to a variable. But nothing works.

Just found the answer, if anyone ran into this again, this is what happened.
The chrome.tabs.query is an async operation so the callback function was called a little bit later. But the document.getElementById('elm').textContent = tt; line was executed before the callback was called and it caused the issue. When debugging this did not happen as the stepping through the code delays the execution of that line.

Related

Show current tab URL in extension's popup

I want to store the current tab url in the following <input> field:
<input type="text" id="pdurl" value=>
The code I wrote in popup.js is the following:
chrome.tabs.query({active : true, currentWindow: true}, function (tabs) {
var tab = (tabs.length === 0 ? tabs : tabs[0]);
var activeTabUrl = tab.url;
});
document.getElementById("pdurl").innerHTML = var activeTabUrl;
I already have the tabs permission set in manifest file.
Your code is totally wrong. There are some serious problems, which would have been easy to avoid checking the popup console for errors. Here they are:
First of all chrome.tabs.query is an asynchronous method. This means that the code is evaluated and queued to be executed; when it executes, the callback function (which you're specifying as second argument) is called and executed. This means that the last line you wrote will always fail because the variable you want to use still doesn't exist (the query function hasn't still finished working).
You are creating a useless (and wrong (because you're assigning an array to it)) variable inside the callback: there's no need of your tab variable, because querying an active tab in the current window always returns a single tab, so you just need to use tabs[0].
Your activeTabUrl variable is defined with the var keyword, which will make it private, and you won't be able to use it outside the function (which is what you're trying to do in the last line, althought it's still wrong).
In the last line you are trying to assign the variable you created in the callback function to the innerHTML of your input: this statement is so wrong on so many levels I don't even know where to start: first of all if you want to actually see something in the text input you have to modify its value; secondly: the variable is not defined; plus, the var statement in your assignment makes it syntactically wrong; fourth: even doing all of the three previous conditions right you still are out of the callback.
Stated the above, this is the correct code for what you're trying to do:
chrome.tabs.query({active : true, currentWindow: true}, function (tabs) {
document.getElementById("pdurl").value = tabs[0].url;
});
I strongly recommend you to take a look at some JavaScript tutorial, and to check the console for errors* before asking questions like this one.
* Errors in the popup of an extension are shown in the popup console, which you can open right clicking on the extension's icon and choosing "Inspect popup".

Can't change value of global variable JS

I have 2 global variables : Lon and Lat
And I want to change the value of these variables inside the function of geolocalisation offered by HTML5:
here is my code :
window.lat={{geo['latitude']}};
window.lon={{geo['longitude']}};
var SK= readCookie('SK');
if(SK==1)
{
navigator.geolocation.getCurrentPosition(function(e){
window.lat=e.coords.latitude;
window.lon=e.coords.longitude;
window.zoomi=15;
})
}
the final value of window .lat is always
window.lat={{geo['latitude']}}
Does anyone know why ?
PS: the SK==1 is true, and inside the function(e), I tried to alert the values and they really change.
but once out of the function, everything vanishes
Javascript is always synchronous and single-threaded so if you are checking window.lat after callback it gets executed even before gelocation call and it has same value.geolocation takes few seconds to get the value and you must write up your code in callback fucntion or write a function to use geolcoation values.
window.lat=1;
window.lon=3;
var SK= 1
if(SK==1)
{
navigator.geolocation.getCurrentPosition(showPosition);
}
//anything written here will be executed before even the getCurrentPosition retuns or sets the results
function showPosition(position)
{
alert("Latitude: " + position.coords.latitude + "-Longitude: " + position.coords.longitude);
//write your code here to use the location
}
here is the jsfiddle http://jsfiddle.net/Xa64Q/ explaining the issue if we are running alert after few seconds it returns correct value
Step through it with a debugger (like Chrome dev tools or Firebug for Firefox). This code works and is the same in principle. It works just fine for this sort of variable assignment:
window.x = 1;
function change() {
window.x = 2;
}
change();
alert(window.x);
My guess is that the getCurrentPosition call is abruptly failing. BTW, that call takes an error call back handler so you should define one for your code.

innerHTML can't be trusted: Does not always execute synchronously

To see the problem in action, see this jsbin. Clicking on the button triggers the buttonHandler(), which looks like this:
function buttonHandler() {
var elm = document.getElementById("progress");
elm.innerHTML = "thinking";
longPrimeCalc();
}
You would expect that this code changes the text of the div to "thinking", and then runs longPrimeCalc(), an arithmetic function that takes a few seconds to complete. However, this is not what happens. Instead, "longPrimeCalc" completes first, and then the text is updated to "thinking" after it's done running, as if the order of the two lines of code were reversed.
It appears that the browser does not run "innerHTML" code synchronously, but instead creates a new thread for it that executes at its own leisure.
My questions:
What is happening under the hood that is leading to this behavior?
How can I get the browser to behave the way I would expect, that is, force it to update the "innerHTML" before it executes "longPrimeCalc()"?
I tested this in the latest version of chrome.
Your surmise is incorrect. The .innerHTML update does complete synchronously (and the browser most definitely does not create a new thread). The browser simply does not bother to update the window until your code is finished. If you were to interrogate the DOM in some way that required the view to be updated, then the browser would have no choice.
For example, right after you set the innerHTML, add this line:
var sz = elm.clientHeight; // whoops that's not it; hold on ...
edit — I might figure out a way to trick the browser, or it might be impossible; it's certainly true that launching your long computation in a separate event loop will make it work:
setTimeout(longPrimeCalc, 10); // not 0, at least not with Firefox!
A good lesson here is that browsers try hard not to do pointless re-flows of the page layout. If your code had gone off on a prime number vacation and then come back and updated the innerHTML again, the browser would have saved some pointless work. Even if it's not painting an updated layout, browsers still have to figure out what's happened to the DOM in order to provide consistent answers when things like element sizes and positions are interrogated.
I think the way it works is that the currently running code completes first, then all the page updates are done. In this case, calling longPrimeCalc causes more code to be executed, and only when it is done does the page update change.
To fix this you have to have the currently running code terminate, then start the calculation in another context. You can do that with setTimeout. I'm not sure if there's any other way besides that.
Here is a jsfiddle showing the behavior. You don't have to pass a callback to longPrimeCalc, you just have to create another function which does what you want with the return value. Essentially you want to defer the calculation to another "thread" of execution. Writing the code this way makes it obvious what you're doing (Updated again to make it potentially nicer):
function defer(f, callback) {
var proc = function() {
result = f();
if (callback) {
callback(result);
}
}
setTimeout(proc, 50);
}
function buttonHandler() {
var elm = document.getElementById("progress");
elm.innerHTML = "thinking...";
defer(longPrimeCalc, function (isPrime) {
if (isPrime) {
elm.innerHTML = "It was a prime!";
}
else {
elm.innerHTML = "It was not a prime =(";
}
});
}

Issue with execution order in javascript

I'm relatively new to javascript so please hold it against me.
I have a bit of code which should give the user a little time to reach the submenu from the base-menu.
My problem is that the code keeps executing in a weird order.
Here is the code:
function onFocusOut() {
var tester = 0;
setTimeout(function(){menuReset(tester)},1000);
}
function menuReset(tester) {
var hoverCheck = function (event) {
alert("#navBase a has focus"); //is fired, but to late...
var tester = event.data.varTester;
var tester = 1;
};
jQuery('#navBase').on('mousemove', 'a', { varTester: tester }, hoverCheck);
jQuery('#navBase').off('mousemove', 'a', { varTester: tester }, hoverCheck);
alert(tester); //This keeps firing first, before the alert in hoverCheck
if(tester == 1){
alert("tester = 1");
return;
}
else {
jQuery('#navBase ul').hide();
jQuery('#navBase').css({'width': ''});
jQuery('#navBaseAnchor').css({
'width': '', 'color': '',
'font-size': '',
'border-bottom-style': '',
'border-bottom-width': '',
'border-bottom-color': ''});
tester = 0;
}
}
Now I keep getting the alert that "tester" is 0, before the hoverCheck function is executed (which should set "tester" to 1) and fires the alert within that function.
Could someone tell me what I'm doing wrong?
I am also fairly new to JS, but should you also be watching out for variable scope errors too?
You have declared tester locally in onFocusOut() and in menuReset(tester), and then called it as a global var outside?
From answers.oreilly.com
LOCAL - Are those that are specific to a function and only work on it.
GLOBAL - Are those that are not defined within a function and may also serve to functions unless the function has not required that
variable.
Nevermind people...
I found a way around it all.
Currently i'm setting a .focus() to the anchor involved on mouseOver. (and of course blur() on mouseleave)
Then it's real easy to check the currently focussed element using document.activeElement.
So problem solved, altough in a bit different way.
alert(tester) is the first line of code that is executing something you notice as a user. The two function calls jQuery().on() and jQuery().off() are only attaching event handlers. If you want to see a "1" in the alert, you have to quickly move your mouse before hoverCheck is executed. But probably you cannot move your hand faster than JavaScript reaching the next line, which is the alert() with tester equals "0".
A little bit different approach would be to set a Javascript timeout() to make the submenu disappear after a certain amount of time if a certain condition isn't met.
Check out this JSFiddle example
Best of luck!

Why windows.onload is executed several times?

I'm binding the window.onload event like this
// It's a little more complex than this, I analyze if there is any other function
// attached but for the sake of the question it's ok, this behaves the same.
window.onload = myfunction;
Onload is triggered twice on my local machine a several times on the production server
If I change it by the jQuery equivalent
$jQuery(window).load(myfunction);
It behaves as expected (executed only once).
Could you help me to understand possible reasons why the first option it's not working as supposed?
Thanks!
The parentheses on your assignment — myfunction() — executes your function. You haven't shown what myfunction does, but this means that the return value from that function is being assigned to window.onload, not the function itself. So, I don't know how that is getting executed, unless you have somehow got that to work, like ending the function with return this;
You want
window.onload = myfunction;
Given the nature of window.onload, it seems unlikely that pure browser events alone are making both calls to myfunction. Therefore, a breakpoint inside your function will help you see the call stack. I've included screenshots for Chrome.
Sample code:
var alertme = function() {
alert("Hello");
}
window.onload = alertme;
function testsecondcall() {
alertme();
}
testsecondcall();
Open your page in Chrome.
After the page has loaded once, open the Developer Tools panel and put a breakpoint on the line inside your function, then refresh the page.
Check the call stack of both times that it breaks. One will be empty (the actual window.onload). The other should give you some information like the following:
On the right, under "Call Stack", you see alertme is called by testsecondcall

Categories

Resources