2

I have always been confused by this, I am not even sure this is possible, but here I go anyways.

If I have a function that increments a value stored in a variable, how do I get the variable outside the function?

Example, this is not great code but I am just playing around.

function scrollVal() {
    var pos = 0;
    $(document).scroll(function() {
        pos = $(document).scrollTop();
        return pos;
    });
}
var scrollPos = scrollVal();
console.log(scrollPos);

I tried the above after something like this failed

var pos = 0;
$(document).scroll(function() {
    pos = $(document).scrollTop();
});
console.log(pos);

I don't see how it's possible to magically update the variable. There has to be a way though. I am trying to use that variable to manipulate an element. Ex

var geometry = new FSS.Plane(1600, 835, pos, 15);

So my question is basically how can I dynamically change a number within a function, then use that outside the function so that I can manipulate an element with the changing value?

EDIT REWORD: I poorly worded this question, what I mean by it fails, is that I cannot get the value inside the scope of the function outside the scope of the function. I want to pass the incremental value as the scrolling takes place into the instance.

Imagine you scroll on a page, and as you scroll something grows, or a color changes. I think this should be easy and it makes me sound like a noob, but I've never done this and I am not sure what rules apply here, and I understand why the variable doesnt update, but I don't know the solution.

Maybe it's not possible to update the value in an instance, but first I need to get that variable to render the appropriate number in realtime, then I can find out.

A good basic example would be appending the variable to an element and watching that number grow as the page is scrolled upon. I have seen this before so I know it's possible, thats what I love about code if you can think it, most likely its possible, but this solution is not straight forward there is a caveat!!! :)

Again this could be made possible if I did something like

var window.pos = 0;
$(document).scroll(function() {
    pos = $(document).scrollTop();
        $('.something').html(pos);
});

Obviously this is made possible by being inside the function, but since I need to pass it to an instance I dont want to execute it over and over..

tshepang
  • 12,111
  • 21
  • 91
  • 136
Michael Joseph Aubry
  • 12,282
  • 16
  • 70
  • 135
  • 1
    Your second one, declaring `var pos` before the callback should work. But you are checking the value via `console.log()` before you change it, immediately after defining it. If you place the `console.log(pos)` inside the `scroll()` callback, you'll see the updated value. Likewise, if you `console.log(pos)` from your browser's console after scrolling the value should have changed. – Michael Berkowski Apr 15 '14 at 00:07
  • Okay thanks that makes sense, I think I am just all over the place. The main thing that got me trying to test the variable was due to the fact it wasnt working inside the instance `var geometry = new FSS.Plane(1600, 835, pos, 15);` so I tried console.logging the variable to see if it was incrementing, and it wasnt. So I thought thats why it wasnt working in the instance – Michael Joseph Aubry Apr 15 '14 at 00:14
  • Sorry guys I think we miscommunicated here. Ill take most the blame I worded the question poorly. I may re open a new one. I am trying to pass the variable into a constructor so that when I am scrolling the element within the instance (flat surface shader) is manipulated in real time as the page is scrolled. – Michael Joseph Aubry Apr 15 '14 at 00:46
  • So the variable would increment as the page scrolls, starting at 0, then (0,1,2,3,4,5,6,7,8) going up without invoking a click function to update it manually. I made it seem like a scope issue that was obviously not the issue above, but its not. Its an issue of getting the value in realtime I should have said outside the function – Michael Joseph Aubry Apr 15 '14 at 00:48
  • Is this at all about scope (availability of a variable), or is the issue that the integer you pass to the `FSS.plane` constructor is in effect passed by value, because it's an integer in JavaScript? Also, I don't get what you mean by "invoking a click function" (in your most recent comment above): if you have some way of updating the `FSS.plane` instance, use that in your `scroll` event handler, where it will be dealt with in the same way as if it were in a `click` event handler. – Jonathan Apr 15 '14 at 01:04
  • What do you mean by "I dont want to execute it over and over"? Would the following be any better? Seems too obvious to warrant suggesting, but I cannot see what else you would mean. var pos = 0; $(document).scroll(function() { var newPos = $(document).scrollTop(); if (newPos != pos) { pos = newPos; $('.something').html(pos) } }) – Jonathan Apr 15 '14 at 01:12
  • I think this is an issue in regard to passing things by reference or by value. Beyond understanding that, if the value returned by `FFS.Plane` does not detect changes to an object (it may not be possible without other tools like watches) there's not much that can be done for live updates. – pseudoramble Apr 15 '14 at 01:13

5 Answers5

0

I'm wondering what you mean by 'something like this failed', which you wrote about this code:

var pos = 0;
$(document).scroll(function() {
    pos = $(document).scrollTop();
});
console.log(pos);

That code will log 0 on the console, because it (1) declares a variable pos, (2) sets its value to 0, (3) assigns a handler to the document's scroll event, and then (4) logs the value of pos. Whenever you scroll, the event handler triggers, and updates the value of pos. But you haven't told it to do anything then. Let's tell it to log the new value of pos:

var pos = 0;
$(document).scroll(function() {
    pos = $(document).scrollTop();
    console.log(pos);
});

Now, when the scroll event is triggered, you change the value of pos, and see it logged.

Addendum 1:

You ask in a comment below about whether pos "should work outside the function still", i.e. is pos available outside the function? The answer is that pos is available within its scope. So for example

function fun() {
    var pos = 0;
    $(document).scroll(function() {
        pos = $(document).scrollTop();
    });
}
alert(pos); // alerts 'undefined' (assuming you've not got another `pos` within this scope)

Whereas:

function fun() {
    var pos = 0;
    alert(pos);
    $(document).scroll(function() {
        pos = $(document).scrollTop();
    });
}
fun(); // alerts '0'

Similarly:

function fun() {
    var pos = 0;
    function bun() {
        alert(pos);
    }
    bun();
}
fun(); // alerts '0'

But I doubt this is helping you much. Perhaps it would be more useful for you to read this: http://web.archive.org/web/20080209105120/http://blog.morrisjohns.com/javascript_closures_for_dummies I hope you enjoy getting to grips with variable scope in JavaScript, it's not complicated but can be tricky when starting out.

Addendum 2:

It sounds like maybe your problem is to do with the reference/value distinction. In JavaScript, integers (and other primitives e.g. booleans) are in effect passed by value rather than reference. It sounds like perhaps you're hoping that by updating the value of pos you're going to have some effect on your FSS.plane instance. This addendum is intended to illustrate why that won't happen, and to show how it in principle could.

So suppose we've got the following:

function Rectangle(x1, y1, x2, y2) {
    this.render = function() {
        // This would draw the plane, but for illustration we'll just use:
        alert(y1)
    }
}

function updatePos() {
    pos = $(document).scrollTop()
}

var pos = 0

var r = new Rectangle(0, pos, 0, 100)

// 'Render' the rectangle, every second
setTimeout(r.render, 1000)

// Update the value of pos according to scroll position
$(window).on('scroll', updatePos)

This will alert 0 60 times a minute. That's because although pos changes, nonetheless inside r we have y1 evaluating to 0: the value 0 was passed to the constructor.

If instead we had

// This time the constructor accepts two objects
function Rectangle(coord1, coord2) {
    this.render = function() {
        alert(coord1.y)
    }
}

function Coordinate(x, y) {
    this.x = x
    this.y = y
}

var point1 = new Coordinate(0, 100)
var point2 = new Coordinate(0, 100)

// Brief and not entirely unrelated intermezzo:
//   Notice that although 0 === 0 and 100 === 100
//   Still new point1 != point2

function updatePos() {
    point1.y = $(document).scrollTop()
}

var r = new Rectangle(point1, point2)

// The next two bits stay the same:

// 'Render' the rectangle, every second
setTimeout(r.render, 1000)

// Update the value of pos according to scroll position
$(window).on('scroll', updatePos)

This time, if the user scrolls down, the 60-times-a-minute message will change.

The following would exhibit the same behaviour, without messing with any constructors.

function Rectangle(x1, y1, x2, y2) {
    this.render = function() {
        alert(y1)
    }
    // New method, a setter for y1
    this.setY1(newValue) = function {
        y1 = newValue
    }
}

var r = new Rectangle(0, 0, 0, 100)

// We update the Rectangle instance directly
function updatePos() {
    r.setY1($(document).scrollTop())
}

setTimeout(r.render, 1000)
$(window).on('scroll', updatePos)
Community
  • 1
  • 1
Jonathan
  • 1,089
  • 1
  • 7
  • 10
  • Well it just returns 0. So `pos` should work outside the function still? I might try to append it to an element to see the numbers increase. I think it might not be working since I am passing it into an instance `var geometry = new FSS.Plane(1600, 835, pos, 15);` – Michael Joseph Aubry Apr 15 '14 at 00:13
  • 1
    "Of course" isn't an explanation. Answers should explain why an issue occurs and why the solution fixes it. – RobG Apr 15 '14 at 00:16
  • The reason why it just returns 0 is because the value of `pos` is only updated when the callback function passed to `scroll()` is invoked. Before the callback is invoked, the initial value of pos is 0. Of course `pos` will work outside the function because it was declared in the global scope. – Akinkunle Allen Apr 15 '14 at 00:16
  • 1
    "it just returns 0", you mean 0 is logged to the console? In that case `$(document).scrollTop()` must be `0`. And yes, `po` will still 'work' outside the function, though not outside its scope. I haven't got space here to explain that so will edit my answer with an example... – Jonathan Apr 15 '14 at 00:16
  • Yeah I am starting to see that. But I am trying to figure out a way to capture the real value, so I can manipulate the element. I have seen this done, I think it may require some extra stuff, but I forgot. – Michael Joseph Aubry Apr 15 '14 at 00:17
  • okay thanks, thats a good idea. Nobody has to spend time answering questions here but it's great helping eachother out, I am sure it will be on point and you will be rewarded, thanks! – Michael Joseph Aubry Apr 15 '14 at 00:18
  • I am not new to javascript at all, I just write in backbone and take the easy route with jQuery. I get scope, this is not a scoping issue. I'll admit I worded the question poorly. The issue is returning the variable to represent the value in realtime. This is where I am confused, this is why I said what I am doing seems kind of illogical, which it is, there has to be a way to get a real time updated value without re-invoking it, just letting the variable render the value ex (0,1,2,4,5,6,7) in realtime. I'll re read you question a few times in case I missed a few things. – Michael Joseph Aubry Apr 15 '14 at 00:42
  • Aah, is it that you want your `FSS.plane` instance to update automatically when the value of `pos` changes? The reason that does not happen is that `pos`, an integer, is (in effect) passed by value rather than reference. Therefore when you update `pos`, there's no effect on the `FSS.plane` instance. This answer might be clearer on this point than what I just said: http://stackoverflow.com/questions/10231868/pointers-in-javascript – Jonathan Apr 15 '14 at 00:51
0

For an example of setting a value at global scope, see this fiddle.

Update:

To see an example of this update occurring live, see this fiddle. Basically, the only difference is that the value is updated whenever the scroll occurs, but still outside the function to show scoping.

Note how var theTop = 0; is set at global scope and it is updated each time the scroll bar is moved. When you click the button, it is updated to the latest value. Javascript code is:

var theTop = 0;
function positionMiddle()
{
    var $highlighted = $('.highlighted');    
    $highlighted.css({        
        left: ($(window).width() - $highlighted.outerWidth())/2,
        top: $(window).scrollTop() + ($(window).height() - $highlighted.outerHeight())/2
    });
    theTop = $highlighted.css('top');    
}

$(window).resize(function(){
   positionMiddle();
});

$(window).scroll(function(){    
   positionMiddle();
});

$('#currentPosButton').click(function(){    
    $('.currentTop').text(theTop);
});

// To initially run the function:
positionMiddle(); 

Now, as to who your methods don't work:

function scrollVal() {
    var pos = 0;
    $(document).scroll(function() {
        pos = $(document).scrollTop();
        return pos;
    });
}
var scrollPos = scrollVal();
console.log(scrollPos);

First of all returning pos from scroll is not correct. When you return a value from a handler it is a boolean to indicate whether events should propagate or not. But, the main problem is that pos will be set in the scroll function, but this has no bearing on the result of scrollVal().

For the second example:

var pos = 0;
$(document).scroll(function() {
    pos = $(document).scrollTop();
});
console.log(pos);

This is closer, but still not quite right. pos will be set when the scroll occurs, but you are logging immediately before any scrolling has even been done. If you did the log on a timer interval, you would see the value of pos change as you scrolled. The terms that you want to read about to understand more are scope and closure. See here and here.

acarlon
  • 16,764
  • 7
  • 75
  • 94
  • Thanks for the effort, I should have been more clear but I am looking for a live update of the value. I am looking for an example.. So as you scroll the number updates. – Michael Joseph Aubry Apr 15 '14 at 00:22
0

I know you can add methods to functions, to make them usable outside of the function.
Here is an example.

Fiddle The fiddle isn't working for some reason. (I knew I should have joined band at school!)

And the increment part is easy.

(Your Variable)++;

OR

(Your Variable)+1;
Ben
  • 2,200
  • 20
  • 30
  • except the only thing is this doesn't really suit my needs. I get this concept, my issue is getting a value outside of the function and get the result in realtime. That logic doesn't make sense, but there is a way there has to be :) – Michael Joseph Aubry Apr 15 '14 at 00:37
  • Lol I get the joke, I would have laughed if I wasnt about to fall asleep, been up too long. Ill rethink it when I wake up and give it a chuckle :) – Michael Joseph Aubry Apr 15 '14 at 00:39
0

I misunderstood your question earlier. I'll take another attempt at answering it. I'll use just JS instead of what I assume is jQuery to demonstrate.

tl;dr - Javascript passes things by value, but objects are always a reference. Therefore you can get a "live update" effect by just looking at its reference instead of a copy of the value. Here's some more detail.

I think what you want is something like so:

function scrollVal() {
  var res = {
    pos : 0
  };

  setTimeout(function() {
    res.pos = 10;
    return res.pos;
  }, 2000);

  return res;
}

var results = scrollVal();
console.warn(results.pos); // This could still be 0 at this point since our first setTimeout handler may have not run

setTimeout(function() {
  console.warn(results.pos); // Assuming this is executed after the first setTimeout, we should see a value of "10" printed
}, 2500);

Probably not exactly what you were looking for. The reason for this elaborate setup is because Javascript passes results by value instead of by reference. This means that an exact copy of the value is returned from your version of the function immediately after execution. You'll never see this "live updated" again because you've got a local copy, and not a reference to it.

However, the thing to realize is that an object is always referred to (meaning, a thing that points to a spot in memory). This means that you get a copy of the actual reference, and not a deep copy of the object. Thus when you refer to this object, you'll look it up in its memory location, and since we modified that in the initial timeout function, it appears to have been "live updated."

EDIT One other thing to point out is that your code using pos in your specified scenario is probably looking for just a value with a type of number. It won't work well with an object, nor will it understand that it's been updated. The only way to truly achieve that kind of behavior is to update the object stored in geometry when you detect that the position has changed (say within the scroll event handler).

Community
  • 1
  • 1
pseudoramble
  • 2,541
  • 22
  • 28
0

Try this:

  var pos = 0;
  function scrollVal() {
    pos=0;
    $(document).scroll(function() {
      pos = $(document).scrollTop();    
   });
   return pos;
 }
var scrollPos = scrollVal();
console.log(scrollPos);

This may help you.

Naveen Chandra Tiwari
  • 5,055
  • 3
  • 20
  • 26