show variable from DOM in Popup.html vice versa [duplicate] - javascript

According to the documentation for chrome.tabs.executeScript (MDN), the callback function accepts an "array of any result" result set from the execution of the script(s). How exactly do you use this to get results? All of my attempts end up with undefined being passed to the callback.
I have tried returning a value at the end of my content script, which threw a Uncaught SyntaxError: Illegal return statement. I tried using the optional code object argument {code: "return "Hello";} with no success.
I feel like I am not understanding what is meant by "The result of the script in every injected frame", in the documentation.

chrome.tabs.executeScript() returns an Array with "the result of the script" from each tab/frame in which the script is run.
"The result of the script" is the value of the last evaluated statement, which can be the value returned by a function (i.e. an IIFE, using a return statement). Generally, this will be the same thing that the console would display as the results of the execution (not console.log(), but the results) if you executed the code/script from the Web Console (F12) (e.g. for the script var foo='my result';foo;, the results array will contain the string "my result" as an element). If your code is short, you can try executing it from the console.
Here is some example code taken from another answer of mine:
chrome.browserAction.onClicked.addListener(function(tab) {
console.log('Injecting content script(s)');
//On Firefox document.body.textContent is probably more appropriate
chrome.tabs.executeScript(tab.id,{
code: 'document.body.innerText;'
//If you had something somewhat more complex you can use an IIFE:
//code: '(function (){return document.body.innerText;})();'
//If your code was complex, you should store it in a
// separate .js file, which you inject with the file: property.
},receiveText);
});
//tabs.executeScript() returns the results of the executed script
// in an array of results, one entry per frame in which the script
// was injected.
function receiveText(resultsArray){
console.log(resultsArray[0]);
}
This will inject a content script to get the .innerText of the <body> when the browser action button is clicked. you will need the activeTab permission.
As an example of what these produce, you can open up the web page console (F12) and type in document.body.innerText; or (function (){return document.body.innerText;})(); to see what will be returned.

Related

How do I change dev tools console scope to be local to a function?

How do I use Chrome dev tools to evaluate code within the local scope of a function? I.e. for the following code
const name1 ="alireza"
function person(){
const name2 ="joe"
}
the result of code manually executed in the console should be
console.log(name2) //"joe"
What you are looking for are breakpoints:
You can dynamically set a breakpoint on a line within the function you want to inspect: devTools will break before the execution of that line and you can evaluate code within the scope of that function in the console.
Alternatively - if you can change the code (which you can also from devTools) - you can put a debugger statement where you want the execution to break.
This is slightly tricky because the statement you want to examine is the only statement in the function.
If you were to add another statement on the following line:
const name1 ="alireza"
function person(){
const name2 ="joe"
const name3 ="bob";
}
Then you could:
Open the Debugger tab
Create a break point on the line after const name2 ="joe"
Open the Console tab
Type person() to run the person function
Wait for the breakpoint to be hit
Return to the Console tab
Type console.log(name2); (or you could just look at the debugging information in the Debugging tab which reports all the in-scope variables).

Why getting "Uncaught ReferenceError: $ is not defined" error on Chrome Dev Console?

I'm trying to run a simple JQuery script in Chrome Developer Console but I have a problem.
There is no problem in this code when I run it on Chrome Developer Console:
var someValue = $("[name='Jack']");
if(someValue !== null){
console.log("Jack is here!");
}
But, I'm getting an error when try to run the same code inside a setTimeout function like below:
setTimeout(function(){
var someValue = $("[name='Jack']");
if(someValue !== null){
console.log("Jack is here!");
}
}, 1000);
Uncaught ReferenceError: $ is not defined
Not only does this happen in setTimeout function, it happens in a normal function as well.
I'm working with latest version of Google Chrome. How can I use JQuery like above in a setTimeout function?
The confusion here is centered on the fact that $ is part of Chrome's Command Line API. When you use $ in your code, you're referring to the Command Line API function named $. You are probably not loading jQuery at all: indeed, your someValue !== null code wouldn't even work with jQuery anyway. You'd need to test for a non-empty jQuery object (someValue.length > 0), not a non-null.
As for why Chrome's $ is accessible in the console but not a setTimeout callback: this appears to be engine-specific magic that limits the command line API to console code only. setTimeout executes its callback in such a way that Chrome cannot be sure the code originated from the console, so it does not grant access to the command line API function named $. Curiously, this isn't typical of JavaScript. Using normal JavaScript scoping rules, the setTimeout callback should have access to the same variables as the surrounding code, regardless of when and where it's executed. The fact that the scope is different one second later is very surprising -- you are right to feel confused!
As a curiosity, a way to simulate this in vanilla JavaScript would be with an object-based scope via with that mutates after the command completes. For example, if every snippet you typed into the console were wrapped with:
var chromeConsoleAPI = { $: function() { ... } }
with(chromeConsoleAPI) {
// your snippet here
}
delete chromeConsoleAPI.$;
In this case, $ is supplied by accessing the chromeConsoleAPI object on the scope chain. Normal code can access $, but since the setTimeout function runs after chromeConsoleAPI.$ is deleted, it does not find anything named $. Note that this still doesn't completely replicate the behavior, because this blocks access to any user-defined $. In actuality, the command line API must inject its functions at the very top (i.e., most remote) part of the scope chain.
The problem because Jquery library Load after your custome code loaded.
Are you using external js file for your custome script?
Then you load your script under the jquery script.
You must add jquery library link first then add your script.

Recommended way to Execute a function stored as String in JavaScript instead of using eval

Before anyone marks it as duplicate, this post does not actually answer the question but suggests a different way altogether to solve that particular issue.
Mine is a different issue. Please let me explain.
In my case, there are various .js files (plugins) which are being loaded with jquery getscript and stored in variables. Then whenever required they will be executed (more than once)
The code for loading script (this code will only run once at the init of the system for each plugin js file)
var storedFunc;
$.getScript(pathToPluginJSFile, function( data, textStatus, jqxhr ) {
storedFunc = data;
});
All the plugins are in this format
(function(){
//lots of code here
})()
But when I checked the storedFunc variable in console, I found out that it has been stored as String variable. Like this,
"(function(){
//lots of code here
})()"
Now to execute this, I used eval, like this (this code can be executed multiple times based on the need)
eval(storedFunc)
Everything is working fine and i am happy with it, but here comes the problem, I read in somewhere that the usage of eval is kind of like a bad thing to do. So now I am afraid that thought everything is working fine, all these negativity of using eval spread on the internet might scare my client away. :(
So, please tell me how I can run that stored function (which has become a string) without using eval.
Or should I use anything else than $.getScript which does not convert a function into a string ?
Or if there is any other way altogether rewriting this plugin functionality?
Please show me the way. I am in need of this solution badly.
Any help will be appreciated.
Thanks in advance.
Understanding how $.getScript works
Seems there is some confusion on how $.getScript works. If you notice jQuery's documentation on the method, and as #Pointy made mention of in the comments, this is stated:
Load a JavaScript file from the server using a GET HTTP request, then execute it.
Here's an example: Let's pretend the contents of the file being returned is only this:
// Contents of yourExternalFile.js
console.log('Executed!');
Now, when you use $.getScript:
$.getScript(pathToPluginJSFile, function( data, textStatus, jqxhr ) {
// The script you retrieved has already executed at this point, and you will find "Executed!" in the console.
console.log('All Done');
});
Console output:
> Executed!
> All Done
The $.getScript method is not meant to be used to return a string of the content of the file. However, while that data is available in the callback, the contents of the file have already been executed. So by taking the string version of the file, and re-executing it with either new Function, or even eval, you are executing it twice on the page (jQuery does it once, and so do you).
Original Post:
Use the Function constructor instead of using eval.
// Your function as a string stored to a variable
var stringFunction = "(function(){console.log('Executed');})()";
// Use the Function constructor to create a new function:
var executableFunction = new Function(stringFunction);
// Now you can execute it
executableFunction(); // logs "Executed"
This snippet from this SO question/answer addresses the difference between eval and new Function.
eval() evaluates a string as a JavaScript expression within the current execution scope and can access local variables.
new Function() parses the JavaScript code stored in a string into a function object, which can then be called. It cannot access local variables because the code runs in a separate scope.
Additional Information (Based on comments)
Yes, you can just get the string contents of the file and store them to a variable without the contents of the file executing. You can have that function to execute anytime. You just need to use the regular get method using jquery, and set the dataType to text. This way, the script will not execute, and you can execute it as you see fit:
var storedFunction;
$.get({url: pathToPluginJSFile, dataType: 'text'})
.done(function (data) {
// Turn the script into a new function and store it
// The information in the script file has not done anything yet
storedFunction = new Function(data);
})
.fail(function () {
console.log('Failed :(');
});
The only thing you will have to watch out for, is making sure that the function was assigned to the storedFunction variable as you are making an api call, and you have to wait for that to finish before attempting to make the function execute.
// Later on, call that function anytime, and as often as you want:
storedFunction();

JavaScript variable not working

I am trying to make a simple Chrome Extension.
At the top of the document, I have put var maxLengthVar;.
I have stored a number using the chrome.storage API. I am getting the value using:
chrome.storage.sync.get('maxLength', function (items) {
maxLengthVar = items.maxLength;
console.log(maxLengthVar);
});
The console.log displays the correct value (2). But when I try to use this variable, I get undefined:
console.log(maxLengthVar);
document.getElementById("textToChange").innerHTML = maxLengthVar;
Note: this code is directly below the first piece of code.
I expect to have the console log the number 2 and the div textToChange's content to change to 2, but, instead, I get undefined in the console and the div stays the same. Why is this?
From what I understand, I have a global variable because at the top of the document, I declared the variable, so what makes the document.getElementById... not read the variable?
Another thing is that even though the console.log's I said above are in the same order in the document, they appear in the console reversed (ie. I get undefined first and then 2.) I know because I added some text ('text' + maxLengthVar) to the console.log's. Why is this?
You need to read up on async methods.
Basically the get method is an async method. While this method is executing the remainder of your script will execute printing out undefined
chrome.storage.sync.get('maxLength', function (items) {
maxLengthVar = items.maxLength;
console.log(maxLengthVar);
});
// <- at this point the async GET method may not have finished executing
// but your code continues anyway
console.log(maxLengthVar);
document.getElementById("textToChange").innerHTML = maxLengthVar;
Your code can be re-written using callbacks
chrome.storage.sync.get('maxLength', function (items) {
maxLengthVar = items.maxLength;
document.getElementById("textToChange").innerHTML = maxLengthVar;
});

console.log not printing the response

I have a function that calls another function over the server and returns a string back which i want to be printed in the browser's log windows, The function looks like:
function getErrorInfo() {
setTimeout(function () {
$.getJSON('Get/ErrorInfo', function (responseText) {
console.log("Log: "+responseText);
});
}, 5000);
}
getErrorInfo();
The function on the server sides does gets hits and returns a valid string But nothing is being displayed in the browser's windows Moreover the function on the server side must get hit after every 5 secs. but it only gets his on time and not again.
Please explain what am i doing wrong here.
Your basic issue is that you need to have properly formatted JSON in order to get back any result. Your result (per above) is:
3/8/2014 5:27:16 PMSystem.NullReferenceException: Object reference not set to an instance of an object. at movie.Models.Genre.GetPosts(Int32 min)
Not only is this an exception text, but it isn't valid JSON. JSON format is fully described here. Rather than calling a real service, I would recommend starting by just getting a static JSON file from the server. Then you know the data is correct.
Side Note:
The other issue here is how you print the OBJECT result from getJSON. When you try to print an object using "Console.log" it converts it to a string, which isn't probably going to show what you want. You should probably change your log statement to:
console.log(responseText);
In chrome at least, the console window will let you browse the contents of the object which can be really helpful. Between the note and the solution above I think you should have it. Best of luck!
When using $.getJSON(), the return result is required to be a valid JSON string, meaning it needs to be parsable into an object or array. In this situation, you can probably simply use $.get(), which will autodetect the return data type, or use $.ajax() and set the dataType: plain if you want to skip the JSON requirement.
On the second issue of keeping the log running, you can call getErrorInfo() from inside the setTimeout() or the callback, and it will keep running:
function getErrorInfo() {
setTimeout(function () {
$.getJSON('/echo/json/', function (responseText) {
console.log("Log: "+responseText);
getErrorInfo();
});
}, 5000);
}
getErrorInfo();
http://jsfiddle.net/Er5Lg/
In my opinion, in this situation, it is better than setInterval(), since that can get backed up and end up overriding calls, and the errors might display out of order.

Categories