I'm only just learning javascript so I imagine this is relatively simple but it's been annoying me for a while now.
I have a function that simply displays some text that I am calling from a AJAX response. Here's my code.
if(this.responseText != null)
{
var text = "<ul>";
var object = eval("(" + this.responseText + ")");
var track;
for (var i = 0; i < object.length; i++)
{
track = object[i];
text += "<li><img src=\"" + track.artwork_url + "\"/>";
text += track.title;
text += "<ul><li>Play</li>"
text += "<li>Download</li></ul>"
text += "</li>";
}
text += "</ul>";
document.getElementById("content").innerHTML = text;
}
function playTrack(track)
{
document.getElementById("content").innerHTML = "This has worked";
}
It's not liking me passing the track object to the function playTrack (a simple function for now that just displays some text). I get the error "Uncaught SyntaxError: Unexpected identifier"
If I pass in track.id (or any other property), it all works fine which I don't understand.
Thanks for any help,
Mister B.
You cannot pass an object like this. track is getting converted to a string and then your playTrack function call fails because the syntax is wrong. It'll be rendered (something like onclick=playTrack([Object object]) which clearly isn't valid JavaScript). What's wrong with just passing the track.id since you know that works?
The #alpian and #Jim is right, you need convert the object to json string, you can use http://www.json.org/js.html to this.
...
text += "<ul><li>Play</li>"
...
When creating the "playTrack" onclick event, you are using the track variable in the wrong way. "track" is an object, and using it as a string will be [Object object]. So in your "html" you'll have something like onclick="playtrack([Object object])" and this is syntactically wrong.
I'd strongly suggest the use of JSON.parse() instead of eval. Instead of building your list entries as text I'd make them as DOM objects with document.createElement() and add the click handlers as addEventListener, that way you wouldn't have the need to try to include your JS objects as text in the HTML stream.
Related
I have a json array being passed to a jade template.
This template then runs through the array adding rows to the html output.
However a new requirement no needs that json object to be passed to a javascript function, so I tried:
- var json = JSON.stringify(rows);
input.medium.success.button.error(type='submit', id='update_details', value= sendCommandLabel, onclick='sendCommand(' + json + ')')
which gives the following output (the full array omitted from brevity):
<input type="submit" id="update_details" value="Send Command" onclick="sendCommand([{"id":"id1;,"param1":value1, ... "}])">
Which is not useful as I am want to use this output in the javascript function and as it stands I can't consume it.
I am hoping I am missing something simple as my other option is to recreate the json array by looping through the objects in the renderer to recreate it!
UPDATE: I modified the code so instead of passing the array to the function, the array is hardcoded into the function while the jade page was being compiled. So this:
function sendStopCommandToAllSensors()
{
var hardcode = !{JSON.stringify(rows)};
became
function sendStopCommandToAllSensors()
{
var hardcode = [{"id":"id1", ... }, {"id":"id2", ... }];
But that still didn't work.
Puzzlingly adding a couple of simple alerts in there showed that there was the correct number of objects (later confirmed that there by cutting and pasting the resultant string directly into code and then manually adding a third object).
alert(hardcode.length); // displays 2
alert("rows: " + hardcode); // displays [object Object],[object Object]
Which is why in the loop that follows the
for (var row in hardcode)
{
alert("row: " + row); // displays 0 or 1, not an object
if (row.active == 1)
{
alert("Reached here"); // never reached
the final line is never reached.
UPDATE II: By stringifying hardcode I can output the human readable json.
alert("rows: " + JSON.stringify(hardcode));
Why is the code not seemingly parsing the array correctly and what to I do need to do correct it?
UPDATE III: I now having it working by using a two step traditional loop and assignment.
for (var i=0; i<rows.length; i++)
{
var row = rows[i];
So the question seems to be now, why didn't the for..in loop work as expected?
I am new to this, but I was going through similar problem I think.
But I am totally ok with JSON.stringify method, which was your first solution. It looks ugly in generated hmtl, but I found it useful in my case.
At least I think I understood it right and you are not trying to do some kind of magic what I can't absorb yet.
if
rows=[{id:id,param:param},{id:id2,param:param2}]
JADE:
- var json = JSON.stringify(rows);
input(type='submit', onclick='myFunction(' + json + ')')
JS:
function myFunction(myRows) {
console.log(myRows[0].id);
console.log(myRows[0].param);
console.log(myRows[1].id);
console.log(myRows[1].param);
.
.
at least it is ok in what I am working on.
I hope I didn't wrote pile of nonsense :)
I'm following w3school beginner tutorial for JS. There's something I don't understand from code below:
<!DOCTYPE html>
<html>
<body>
<p id="demo"></p>
<script>
var cars = ["Saab","Volvo","BMW"];
var text = "";
for(var i = 0; i < cars.length; i++) {
text+=cars[i] + "<br>";
}
document.getElementById("demo").innerHTML = text;
</script>
</body>
</html>
Can someone explain me the logic of text+=cars[i]? I understand that += means increment, but I canĀ“t understand the logic behind adding the array element to variable text.
Thank you so much for your quick replies! I've got a follow up question: is there an alternative to display the same type of information with having to use the
var text = "";
and
text+=cars[i]
pieces of code? If so, how would the snippet of code look like and what should I insert into HTML if not
text
?
Thanks again!
a+=b is short for a=a+b. In your case you have text = text + cars[i] + "<br>".
text is a string, and you are using + to append a value from the array (that contains strings), and then append "<br>"
The value of text at the end of the loop is going to be
Saab<br>Volvo<br>BMW<br>
where br stands for line break. So that each of them gets printed on new line.
And the last line of code
document.getElementById("demo").innerHTML = text;
changes the value of html element which has id of demo to that of text.
text += cars[i] + '<br>';
Concatenates element i of the cars array to the text, separated by a <br> tag.
Consider it like this,
text+=cars[i] + "<br>";
is actually
text=text+cars[i]+"<br>";
so that rather than deleting the old value it will concatenate the new word with existing string.(String Concatenation).
PS:As a fellow beginner a small piece of advice rather than following W3 Schools please go to site like codecademy which helps you to learn along with practice and proper explanation.
Don't think of += as incrementing, that's ++.
a = a + b
a += b;
Those two statements are the same. The bottom one takes the variable on the left side (a) and appends the right side to it (b), and then assigns it all back to he left side (a). So it's just a shorthand.
So what you're doing in your code is appending the string from cars[i] to your variable text.
This would do the same thing:
text = text + cars[i] + "<br>";
Once the loop runs, you will have the following in text:
Saab<br>Volvo<br>BMW
In javascript + is used for string concatenation
The code
for(var i = 0; i < cars.length; i++) {
text+=cars[i] + "<br>";
}
is picking each element from the array and concatenating "" to it.
If you console log the text before setting the innerHTML, it looks something like this -
"Saab<br>Volvo<br>BMW<br>"
here they do it actually just to show theres no point of doing it this way they just wanna show to reach each individual inside an array and concatanate it into a string the very same thing you can do with Array.prototype.join() method dont think here that u must use as they show always concatanate into a string if you want you can simply use every single individual inside as u wish as well
+= is not increment. It's adding (in this case concatenation) and saving result in the same variable.
var a +=b;
Is the same to:
var = a + b;
In your case script concatenates all array elements into one string and adding <br> tags between them.
I have a (GIS) project which displays large amounts of customer data (Thousands of records) to clients. Where nescessary/possible/required, we use server side pagination/filtering/data manipulation but there are cases where it is most efficient to send the data in JSON format to the client and let their browser do the filtering.
The amount of data is large, so we format it to save on bandwidth and parsing time - instead of individual objects, we send a structure that includes the attribute names first and then the values in a single flat array. On the client, we rebuild this into more traditional json objects before other processing occurs. eg:
{attrNames:["foo","bar"],values:[1,2,3,4,...]) -> [{foo:1,bar:2},{foo:3,bar:4},...]
The code for doing this looks a little like this:
function toObjectArray(attrNames, values){
var ret = [];
var index = 0;
var numAttrNames = attrNames.length;
var numValues = values.length;
while(index < numValues){
var obj = {};
for(var a = 0; a < numAttrNames; a++){
obj[attrNames[a]] = values[index++];
}
ret.push(obj);
}
return ret;
}
Given that the attributes may change depending on the customer data, is there a way to do this translation that takes advantage of hidden classes in modern javascript engines like V8? I have done some micro benchmarks similar to our use case ( http://jsfiddle.net/N6CrK/1/ ) where working with json such that hidden classes are used is orders of magnitude faster than building the objects as above. I can get some of this boost using "eval" to create objects, but this feels ugly (This is demonstrated in the js fiddle). Is there a better way? Perhaps using some variant of Object.create, or something like it?
You mean something like this right?
function toHiddenObjectArray(attrNames, attrValues){
var numAttrNames = attrNames.length,
numValues = attrValues.length;
function Data( values ) {
for(var v = 0; v < numAttrNames; v++) {
this[attrNames[v]] = values[v];
}
}
var ret=[];
for( var i=0; i<numValues ; i+=numAttrNames ) {
ret.push( new Data( attrValues.slice(i,i+numAttrNames) ) );
}
return ret;
}
You can check our the fiddle here: http://jsfiddle.net/B2Bfs/ (With some comparison code). It should use the same "Hidden Class" (i.e. Data). Not sure how much quicker it is though!
But, if you really want to make your code none blocking, why not load the page, then request the data via AJAX, then run all you code when you get a response.
I can get some of this boost using "eval" to create objects, but this feels ugly
There's a less ugly way using the Function constructor. Also, further optimisations can be done by immediately assigning the values to the properties, instead of initialising them with null and then again iterating through the attrs array like the adHoc does it. You'd just pass each of the rows you get in the response (array? string? byte-whatever?) as a parameter to the factory.
Also I've moved the creation of the factory function out of the create function, so that only one function will be instantiated (and optimized after enough calls to it).
A decent amount of the time in your test loop is spent on the getTotal, so I've optimised this in a similar manner. Not using getTotalAdHoc in testing the optimised solution drastically reduces the measured time (you can test with getTotalOptimum as well).
var factory = new Function("arr", "return{"+attrs.map(function(n, i){
return n+":arr["+i+"]";
}).join(",")+"};");
var getSum = new Function("o","return "+attrs.map(function(n, i){
return "o."+n;
}).join("+")+";");
(updated jsfiddle)
I haven't yet tried moving the complete loop into the generated code, which could avoid a few function calls, but I don't think this is necessary.
For some reason I just recalled this question... and I just came up with a solution that is way dirtier than using eval but which causes a huge speed boost. The downside of it is that code will be similarly little maintainable as when using eval.
The basic idea is: When receiving the attribute names, generate the function code to parse the following data in JavaScript and add it in a <script> tag to the <head>.
Yeah, isn't that dirty? :-)
If performance is so critical for you, it will definitely help you... here's a modified version of your microbenchmak that proves it: http://jsfiddle.net/N6CrK/17/
Some remarks on the code...
The two functions createWithGeneratedFunction and getTotalWithGeneratedFunction are simply wrapper functions that can be used by productive code. All they do is make sure that the <script> with the generated functions is set up and then call it.
function createWithGeneratedFunction(numValues){
makeSureScriptsAreSetUp()
return createWithGeneratedFunctionAdded(numValues);
}
function getTotalWithGeneratedFunction(objs){
makeSureScriptsAreSetUp()
return getTotalWithGeneratedFunctionAdded(objs);
}
The actual workhorse is the makeSureScriptsAreSetUp with the functions it creates. I'll go through it line by line:
function makeSureScriptsAreSetUp() {
if(scriptIsSetUp)
return;
If the required <script> tag was already set up this function will directly return since there is nothing to do for it anymore.
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
var theFunctions = "";
This prepares the creation of the required functions. The theFunctions variable will be filled with the code that is going to be put into the <script> tag content.
theFunctions =
"function createWithGeneratedFunctionAdded(numValues) {" +
" var ret = [];" +
" var value = 0;" +
" for(var i = numValues; i-- > 0;) {" +
" ret.push({";
for(var attr in attrs) {
theFunctions +=
" " + attrs[attr] + ": value++,";
}
theFunctions +=
" });" +
" }" +
" return ret;" +
"}" +
"";
This completes the code for the parsing function. Obviously it just "parses" the numbers 0 to numValues in this microbenchmark. But replacing value++ with something like TheObjectThatTheClientSentMe.values[value++] should bring you very close to what you outlined in your question. (Obviously it would make quite a lot of sense to rename value to index then.)
theFunctions +=
"function getTotalWithGeneratedFunctionAdded(objs) {" +
" var ret = 0;" +
" for(var i = objs.length; i-- > 0;) {" +
" var obj = objs[i];" +
" ret += 0";
for(var attr in attrs) {
theFunctions +=
" + obj." + attrs[attr];
}
theFunctions +=
" ;" +
" }" +
" return ret;" +
"}";
This completes the code for the processing function. Since you seem to require several processing functions, especially this code could become somewhat ugly to write and maintain.
script.text = theFunctions;
head.appendChild(script);
scriptIsSetUp = true;
}
In the very end we simply set the <script> tag content to the code we just created. By then adding that tag to the <head>, Chrome's hidden class magic will occur and will make the code VERY fast.
Concerning extensibility: If you have to query different attribute/value sets from the server on the same page, you might want to give each parsing/processing method set unique names. For example, if you first receive attrs = ["foo","bar"] and next attrs = ["foo","bar","baz"] you could concat the underscore-joined attribute name array to the generated function names.
For example, instead of using createWithGeneratedFunctionAdded you could use createWithGeneratedFunctionAdded_foo_bar for the first attribute/value set and createWithGeneratedFunctionAdded_foo_bar_baz for the second attribute/value set. An attr parameter could then be added to the wrapper functions that will be used to generate the correct code line for an eval (yes, here the evil eval would return) to trigger the correct generated function. Obviously, the attr parameter would also be required for the makeSureScriptsAreSetUp function.
I'm appending some html codes on javascript. I'm also binding an onclick to it. The problem is, I'm getting a javascript error each time I press onclick. I'm passing a string to the function onclick. Here's a clearer view.
var propertyTypeDdlValues = "";
propertyTypeDdlValues += "<li onclick='selectPropertyType('Condominium/Studio')'>Condominium/Studio</li>";
$("#propertyTypeDdl").html(propertyTypeDdlValues);
This is my selectPropertyType
function selectPropertyType(cattype){
$("#propertyType").text(cattype);
$("#hdnPropertyType").val(cattype);
}
I keep on having this error:
Uncaught SyntaxError: Unexpected token }
I think the problem is how I wrap strings around (" "). What made me say this? Because when I try to just use a simple function like this:
propertyTypeDdlValues += "<li onclick='displayMessage()'>Condominium/Studio</li>";
function displayMessage(){
alert("Message");
}
It goes through the function and the alert message is being displayed.
Any ideas? Thanks!
Here is a solution:
JSFiddle
$(function () {
$("#propertyTypeDdl").append("<li onclick=
selectPropertyType('Condominium/Studio')>Condominium/Studio</li>");
$("#propertyTypeDdl").append("<li onclick=
'displayMessage()'>Condominium/Studio</li>");
});
function selectPropertyType(cattype) {
$("#propertyType").val(cattype);
}
function displayMessage() {
alert("Message");
}
One way would be to store that string in a variable and passing that into the function. But I guess some one else will give a better answer.
Modify your propertyTypeDdlValues to
propertyTypeDdlValues +=
"<li onclick='selectPropertyType('\'Condominium/Studio\'')'>
Condominium/Studio
</li>"
You are using jQuery.
So why not:
foreach(thing in listOfThings) {
var li = $('<li>' + thig.name + '</li>');
li.click(function(e) {
selectPropertyType($(this).html());
});
$("#propertyTypeDdl").append(li);
}
It's a lot cleaner.
Friendly advice: never ever use HTML on/whatever events again :)
Your original string
"<li onclick='selectPropertyType('Condominium/Studio')'>Condominium/Studio</li>"
Gets parsed as this HTML:
<li onclick='selectPropertyType('Condominium/Studio')'>Condominium/Studio</li>
The onclick atrribute ends just after the left bracket, leading to invalid javascript. Since you have three levels of nesting, you'll need to escape your quotes:
"<li onclick='selectPropertyType(\'Condominium/Studio\')'>Condominium/Studio</li>"
or
"<li onclick='selectPropertyType(\"Condominium/Studio\")'>Condominium/Studio</li>"
but it is better to avoid pieces of javascript code as strings (and, since you're using jQuery, avoid onclick attributes altogether), and create an element and set its click handler to a function. This will also avoid the need to nest string literals:
var $li = $("<li>").text('Condominium/Studio').click(function(){
selectPropertyType('Condominium/Studio')
});
$propertyTypeDdlValues = $propertyTypeDdlValues.add($li)
I am trying to check a series of buttons to see if they have been selected in order to generate a query string. So far as I can tell, the logic looks something like this:
if ($("input[name='ovr']")[0].checked)
{
...
}
This works, but since I don't want to use a series of if statements to generate the string (because more buttons might be added later, it's inefficient, etc), I generated an array representing the names associated with the buttons (I printed the array; it definitely contains the proper names). When I made a loop to run through each name in the array, however, the console suggested that 'input' was an unrecognized expression. Here is the loop:
for (i = 0; i < myList.length; i = i + 1) {
if ($("/"input[name='" + myList[i] + "']/"")[0].checked) {
string += myList[i] + "=" + myList[i] + "&";
}
}
string is a variable that appends the proper signature(?) onto itself if the button check should return true. myList is the array containing the names.
Unfortunately, I am new to all of this and don't really know where to begin. I did try to google the answer (really a novice here) but I wasn't even quite sure what to search for. I also messed around with the phrasing of the input expression, used different escape characters, etc., to no avail. Neither the HTML nor most of the javascript is mine (it's my supervisor's), so I am doubly lost. Any suggestions you could give would be sincerely appreciated.
Thanks!
Something like this would work (I don't really understand why you tried to add those backslashes, so I only show you the right way):
for (i = 0; i < myList.length; i = i + 1) {
if ($("input[name='" + myList[i] + "']")[0].checked) {
string += myList[i] + "=" + myList[i] + "&";
}
}
One note: if you are generating a query string, don't forget to use encodeURIComponent()!
something like this?
var query = "?";
$('input:checked').each(function(index) {
query += this.name + "=" + this.value + "&";
});
you can modify the selector to get only the checkboxes you need.