JavaScript variables - new var altering original value - javascript

It's been a while since I've touched JavaScript, but something has got me flummoxed, simple I just know I should know it, but it's causing me a massive headache for the past hour!
Scenario;
Mop = {
els : [],
pool : [],
generate : function(){
for (var i=0; i<Mop.els.length; i++){
Mop.pool.push(i)
}
},
oneAtime : {
p : Mop.pool,
runit : function(){
timerID = window.setTimeout(function(){
Mop.show()
});
}
},
show : function(){
var p = Mop.oneAtime.p;
var r = Math.floor(p.length * Math.random());
p.splice(r,1);
},
init : function(){
Mop.els = $(".cells");
Mop.generate();
Mop.oneAtime();
}
}
Mop.init()
A long winded way to say it's a problem with variable assigning. But I thought I'd show the context.
In short, invoking Mop.show() affects both the arrays Mop.pool, and Mop.oneAtime.p, even though the latter was assigned Mop.pool value, and Mop.pool is not touched again. Mop.pool must remain un-altered.
I can hear someone laughing at how easy this must be - but I just cannot work out why both arrays are affected by the splice() method! Scope, closure, something like that I bet, I've tried all I can think of...

Related

How do I add up a number to a properties in my object?

I have an object like this:
var statistics = {
won: 0,
tie: 0,
lost: 0
};
I have a function that adds 1 to won:
var plus1 = function() {
return statistics.won++;
}
I call that function within an if/else statement like this:
plus1();
But it doesn't work. Does anyone have an idea?
It's probably that x++ returns x instead of x+1.
You are looking for
var plus1 = function() {
return ++statistics.won;
}
Looking at your code I don't really see any reason why you would return your result.
I would rewrite the function to simply be
function plus1() {
statistics.won++;
}
When it comes to having it update, I can't see any were in your code where you actually update the html. After you've run plus1(). If I run console.log(statistics) in my Console I can see that statistic.won goes up whenever I win.
As already mentioned in the comment above, if you run wins() after you've run plus1() it will all work.
This is due to to way pre/post incrementation works in JavaScript:
var one = 1;
var two = 1;
// increment `one` FIRST and THEN assign it to `three`.
var three = ++one;
// assign `two` to `four`, THEN increment it
var four = two++;
So in your code, you're assigning the value of statistics.won to the return value first and then incrementing it. You can see the difference in how they work here.
So, as I mentioned in the comments, return ++statistics.won; is the solution you need.

jQuery .click command order

This is a very simple question/problem, and I can easily work around it, but since I'm learning javascript I was very eager to know WHY exactly this particular problem is happening.
$("#go").click(function() {
$("p").append(array[x] + " ")
functionlist[array[x]]()
x++
})​
This does not work as I expect it to. I want it to write the current content of array, perform a simple animation that is associated with a certain function name, and then increment x. Everything works, except it doesn't increment x.
If I do this:
$("#go").click(function() {
$("p").append(array[x] + " ")
//functionlist[array[x]]()
x++
})​
x is incremented successfully.
So my question is, why does this happen?
Here is a link to a jsfiddle that I am using: http://jsfiddle.net/mxy6N/3/
Well, if you check your script console (F12 is most browsers), you'll see that functionlist[array[x]]() throws an error something like:
Object has no method "smooch"
This is because array[x] is equal to "smooch", and functionlist["smooch"] is undefined, so it errors out before it makes it to your x++.
Other things going on in this code:
x is declared inside of your function, therefore it will always be 0 at the time it is used.
even if it were declared outside of your function, as you increment it, it will run out of items to look at in your array. You'll need to use a modulo operator or two here.
You're not referencing an .js files that have a definition for $.fn.transition, so your transition calls will also error out.
flip and flop both have the same rotateY value, so once it "flips" it won't "flop"
Here is something that might do what you're looking to do: http://jsfiddle.net/g5mJd/3/
And the updated code:
var array = ["smooch", "jab", "flip"];
var move = "flip";
var x = 0;
var functionlist = {
flip: function() {
$("#block").transition({
perspective: '0px',
rotateY: '180deg'
}, 1000);
},
flop: function() {
$("#block").transition({
perspective: '0px',
rotateY: '0deg'
}, 100);
}
};
$("#go").click(function() {
var functionName = (x % 2 == 0) ? 'flip' : 'flop';
var fn = functionlist[functionName];
if($.isFunction(fn)) {
fn();
}
var say = array[x % array.length];
$('p').append(say + ' ');
x++;
})​;
EDIT: Updated with $.isFunction() check.

Issue with Javascript For loop

Consider the Code below:
function splicer()
{
var arrayElements = ["elem1","elem2","elem3","elem4"];
for(var index in arrayElements)
{
arrayElements.splice(index,1);
}
alert("Elements: "+arrayElements);
}
The above function is supposed to remove all the elements from the array "arrayElements". But it won't.
Javascript engine maintains the "index" as it is and doesn't mind the array being modified.
People might expect something like "for each" loop that doesn't have this kind of issue
even the following code doesn't seem to work:
function splicer()
{
...
for(var index in arrayElements)
{
arrayElements.splice(index--,1);
}
...
}
even when changing the value of the variable "index" doesn't seem to work.
the changed value is available inside the "for(...){...}" block but, as the loop reaches the next iteration, the value gets reset and continues from the next index as clockwork.
so it seems code like this might be the only solution:
function splicer()
{
var arrayElements = ["elem1","elem2","elem3","elem4"];
for(var index=0;index<arrayElements.length;index++)
{
arrayElements.splice(index--,1);
}
alert("Elements: "+arrayElements);
}
Tested in: Firefox 16 Beta.
But placing a unary Operator inside a "splice()" method seems to be misleading at first sight.
This might be worth considering to the "W3C" or whomever it may concern so that they come up with a nice solution.
You may want to refer to John Resig's array.remove() link.
// Array Remove - By John Resig (MIT Licensed)
Array.prototype.remove = function(from, to) {
var rest = this.slice((to || from) + 1 || this.length);
this.length = from < 0 ? this.length + from : from;
return this.push.apply(this, rest);
};
Try this:
*Splice modifies the original array, hence tge loop skips the alternate values. *
var arrayElements = ["elem1","elem2","elem3","elem4"];
arrayElements.splice(0,arrayElements.length);
alert("Elements: "+arrayElements)

Avoiding having to write the same word over and over again

I'm very new to javascript so this question might sound stupid. But what is the correct syntax of replacing certain words inside variables and functions. For example, I have this function:
function posTelegram(p){
var data = telegramData;
$("#hotspotTelegram").css("left", xposTelegram[p] +"px");
if (p < data[0] || p > data[1]) {
$("#hotspotTelegram").hide()
} else {
$("#hotspotTelegram").show()
}
};
There is the word "telegram" repeating a lot and every time I make a new hotspot I'm manually inserting the word to replace "telegram" in each line. What would be a smarter way of writing that code so that I only need to write "telegram" once?
Group similar / related data in to data structures instead of having a variable for each bit.
Cache results of calling jQuery
Use an argument
function posGeneral(p, word){
// Don't have a variable for each of these, make them properties of an object
var data = generalDataThing[word].data;
// Don't search the DOM for the same thing over and over, use a variable
var hotspot = $("#hotspot" + word);
hotspot.css("left", generalDataThing[word].xpos[p] +"px");
if (p < data[0] || p > data[1]) {
hotspot.hide()
} else {
hotspot.show()
}
};
You can't always avoid this kind of repetition (this is general to all programing languages).
Sometimes, you can make generic functions or generic classes, for example a class which would embed all your data :
Thing = function(key, xpos) {
this.$element = $('#hotspot'+key);
this.xpos = xpos;
};
Thing.prototype.pos = function (p, data) {
this.$element.css("left", this.xpos[p] +"px");
if (p < this.data[0] || p > this.data[1]) {
this.$element.hide()
} else {
this.$element.show()
}
};
And we could imagine that this could be called like this :
var telegramThing = new Thing('telegram', xposTelegram);
...
telegramThing.pos(p, data);
But it's really hard to make a more concrete proposition without more information regarding your exact problem.
I recommend you read a little about OOP and javascript, as it may help you make complex programs more clear, simple, and easier to maintain.
For example, using a Thing class here would enable
not defining more than once the "#hotspotTelegram" string in your code
reusing the logic and avoid making the same code with another thing than "telegram"
not having the Thing logic in your main application logic (usually in another Thing.js file)
But don't abstract too much, it would have the opposite effects. And if you don't use objects, try to keep meaningful variable names.
var t = "Telegram";
var $_tg = $('#hotspotTelegram');
$_tg.css("left", "xpos"+t[p] + "px"); // not sure about this line, lol
$_tg.hide();
$_tg.show();
etc.
you can create a selector as variable, something like this
function posTelegram(p){
var data = telegramData;
var $sel = $("#hotspotTelegram");
$sel.css("left", xposTelegram[p] +"px");
if (p < data[0] || p > data[1]) {
$sel.hide()
} else {
$sel.show()
}
};

setInterval and displaying objects in an array against a timeline

I need help creating an efficient JS listener against an array of objects and a video. It works, but is really inefficient and memory intensive, especially if the array of objects is large.
I have a video that is 1 minute long and I want to display objects that are synchronized with this video on the GUI based on timecodes. For example, at 0:23, I want to display a comment.
The synchronized, timecoded objects are stored in an array like this:
comm1 = {text : "I like it", time : 23}
comm2 = {text : "I hate it", time : 48}
comm3 = {text : "I love it", time : 59}
var arr = [comm1, comm2, comm3]
I then have a Flash video player that returns the current playhead position of the video. I can call it through JavaScript like so:
var position = document.getElementById("player").currentTime();
I then created a setInterval object that loops through the array every 250 ms to display the comment like so:
function getCurrentComment(){
for(i=0;i<arr.length;i++){
if(position >= arr[i].time){
getId("comments").innerHTML = arr[i].text;
}
}
}
var commListener = setInterval("getCurrentComment()", 250);
This works visually, but in reality, it continues to execute the "getId("comments").innerHTML = arr[i].text" line of code every 250 ms no matter what. Is there better way to do this without having to continuously loop through an array? It gets really bad when there are 100+ objects in the array. I was looking at the Observer Pattern but I don't think it will work for this since I'm synchronizing in real time with a video.
If you can listen to the seek event, then you can do something like this:
var arr = [
{text : "I like it", time : 23},
{text : "I hate it", time : 48},
{text : "I love it", time : 59}
],
ceil = arr.length - 1,
current = 0,
el = getId("comments"),
commListener;
function commListenerFunc()
{
commListener = setInterval(function ()
{
if (arr[current].time == position)
{
el.innerHTML = arr[current].text;
if ( current < ceil )
{
current++;
}
else
{
clearInterval( commListener );
}
}
}, 250);
}
// Call this function on the seek event
function seekCallback()
{
clearInterval( commListener );
current = 0;
for ( var i = 0; i < ceil; i++)
{
if ( arr[i + 1].time < position )
{
current++;
}
else
{
break;
}
}
commListenerFunc();
}
commListenerFunc();
Please note: I did not debug this code, and it might have some issues. I'm just posting it here to give you the general idea...
If you sort your arr and set var nextComm = 0, you can just test against the arr[nextComm]. If that one gets displayed, increment nextComm. If there's a seek, find the appropriate nextComm by seeking through arr for the closest future match.
Also, don't set innerHtml when you don't have to.

Categories

Resources