2

How do I store the window.innerWidth value and calculate the difference with the new value on resize?

var w;

function example {

var onResize = function () {

    w = {width: window.innerWidth || document.body.clientWidth };

if (difference of old width and this (new) width is 100) {
// then do some stuff
}

window.addEventListener('resize', onResize);
onResize();
}

The solution would be to store the variable inside a global or outside on onResize, calculate the difference between the old and new data, do a condition of some sort: (old width - new width) = 100? and if the difference is 100 then do some stuff.

Now conceptually I know how to achieve this, but how can I actually go about implementing it in JavaScript?

Asperger
  • 3,064
  • 8
  • 52
  • 100
  • I'm not sure I see where your barrier is; you actually seem to have a pretty good idea of how to do it. In case it gets you past your mental block, a hint: You will be using subtraction, and you will have to account for a possibly negative value. (Also, I think you want normal parentheses where you have curly braces) – Katana314 Sep 17 '15 at 20:46
  • @Katana314 yes because substracting gives me the exact difference. Now having all this in mind I somehow seem blocked. If I do a check like this if (w - w == 100) if the difference between old and new is 100 then do something. I just dont know if that will work or if JS will interpret it as old or new subtracting from itself – Asperger Sep 17 '15 at 20:49
  • so I basically need JS to be able to differentiate both "w" in my conditional – Asperger Sep 17 '15 at 20:50

3 Answers3

2

Just start out by using some better names and an extra variable as a placeholder. Also, I am adding a scope to your shown code so that w or width does not get used outside of this snippet.

(function(){
    //create a closed scope to prevent naming collision        

    //store the previous value for the width
    var previousWidth = window.innerWidth || document.body.clientWidth;

    var onResize = function () {

        //calculate the current value for the width
        var currentWidth = window.innerWidth || document.body.clientWidth;

        //look to see if the change was more than 100
        //using the absolute value here is important because it ensures
        //that a change of -150 is also a difference of more than 100
        if (Math.abs(previousWidth - currentWidth) > 100 ) {
            // then do some stuff

            //change the prior width to the current for for future references
            previousWidth = currentWidth;
        }
    };

    window.addEventListener('resize', onResize);
})()

A complete refactor. Let's examine a more structured approach, where we accumulate the size of the resize as it occurs, and also look at a historical value. This will allow us to not only track if it moved in segments, but how it moved in general, a speed could be calculated, and also include some granularity.

jsFiddle Demo

(function(){
    //create an ExecutionContext to ensure there are no
    //variable name collisions

    //store the value of the first width of the window
    var starting = getWidth();

    //make this a function since it is called so often (DRY)
    function getWidth(){
        return window.innerWidth || document.body.clientWidth;
    }

    //this is a flag to determine if a resize is currently happening
    var moving = false;

    //this value holds the current timeout value
    var tracking = null;

    //this function allows for the resizing action to throttle
    //at 450ms (to ensure that granular changes are tracked)
    function throttle(callback){
        //setTimeout returns an integer that can later be called
        //with clearTimeout in order to prevent the callback from executing
        tracking = setTimeout(function(){
            //if this executes, it is assumed that the resize has ended
            moving = false;
            //checks to ensure calling the callback wont cause an error
            if(toString.call(callback) == "[object Function]")callback();
        },450);
    }

    //simple log in here but this will allow for other code to be placed
    //to track the beginning of the resize
    function resizeStart(){
        console.log('resizing started');
    }

    //same as the start, this will execute on finish for extension
    //purposes
    function resizeEnd(){
        console.log('resizing finished');
    }

    //this will be used to test against where the resize width value
    //started at
    var beginning = getWidth();

    //this array will hold the widths of the page as measured during
    //each resize event
    var steps = [];

    //seed it with the first value right now
    var step = getWidth();
    steps.push(step);

    //these will hold the granular changes which are the differences
    //between each concurrent step, only while a resize is active
    var changes = [];

    //the resize event handler
    window.addEventListener('resize', function(){
        //if a resize event is not active then call
        //the start function, set the width of the beginning variable
        //as an anchor so to speak, and make sure to mark resize as active
        //with the moving flag
        if(!moving){
            resizeStart()
            beginning = getWidth();
            moving = true;
        }else{
            //if the resize event is already active (moving) then clear out
            //the ending time so that it can be reconstructed since
            //we are still collecting information
            clearTimeout(tracking);
        }

        //this change will be the difference between the width now
        //and the width of the last reading
        var change = getWidth() - step;

        //once the change is calculated we update the current width reading
        //of the step
        step = getWidth();

        //and then store both of them (remember changes is only populated
        //while resize is active)
        changes.push(change);
        steps.push(step);

        //this sets up the timeout as noted above
        throttle(function(){
            //the reduce is just suming, so this takes the sum of
            //the changes array
            var totalChange = changes.reduce(function(a, b) {
                return a + b;
            });

            //if the sum was positive then the resize made the window larger
            var direction = totalChange > 0 ? "larger" : "smaller";

            //the assumption if this callback function is executing is that 
            //the resize event has finished, and so the changes array 
            //is reset
            changes = [];

            //this gets back to the very original question of when
            //there had been 100 pixels of change
            if(Math.abs(totalChange) > 100){
                console.log(Math.abs(totalChange) + " pixel granular change " + direction);
            }

            //this just logs what the overall change has been this whole time
            console.log(Math.abs(steps[steps.length-1] - starting)+" pixel overall change");

            //again noting that resize has ended, reset the "anchor" width
            beginning = getWidth();

            //and finally call our ending function
            resizeEnd();
        });
    });
})()
Travis J
  • 81,153
  • 41
  • 202
  • 273
  • can still get incremental changes during manual resize that will never have difference >100 using this approach yet over the course of resizing the total user changes can be much greater than 100 – charlietfl Sep 17 '15 at 20:54
  • @charlietfl - *If* that were the intention here, then the simple change would be to clamp the previous width and not change it. However, that was not really made clear in the original question. Dealing with such nuance would require modification from the original statement of checking for a 100 pixel difference, but this approach will still work with small modification depending on the level of nuance in the resizing. – Travis J Sep 17 '15 at 20:59
  • the way @pawel does it works somehwat, don't reset the baseline unless it reaches 100. A timer is probably needed to get OP's ultimate goal I think – charlietfl Sep 17 '15 at 21:03
  • Interesting. I like how this is written – Asperger Sep 17 '15 at 21:06
  • @charlietfl - So you would rather see it as a sliding window? Like this? It really depends on the actions required for the expansion/contraction. – Travis J Sep 17 '15 at 21:06
  • @TravisJ agree... OP really needs to clarify expectations. Resizing window slowly will keep changing `previous` and difference will always be small is my whole point. If the 100 is never reached... and user starts changing again...previous will be off a bit but could be adjusted within a setTimeout – charlietfl Sep 17 '15 at 21:08
  • @TravisJ hello. Let me clarify. The user resizes the window slowly. My intention does require a change of the previous value after every 100 of difference so that it can do a fresh calculation. – Asperger Sep 17 '15 at 21:15
  • @Asperger and what if the full 100 is never reached? And user starts changing again? – charlietfl Sep 17 '15 at 21:16
  • @charlietfl Lets say the user starts with a window size of 100% and drags the edge to gradually decrease the viewport. The 100 is always reached at some point. There will always be a difference of 100 at some point if the old value is updated. So the new value always becomes the old value and succesively. – Asperger Sep 17 '15 at 21:17
  • @Asperger not necessarily...user could go 95 and stop. Then move more later. Not clear in your requirements if it is for `each` resize or just whenever window changes at any time by 100 – charlietfl Sep 17 '15 at 21:19
  • @charlietfl for each resize. Lets say my window previous value initiates with 10 and then I scale down to 8. I get a difference of 2. I scale down further and the 8 becomes my previous value. Then I scale down further past and 6 and then I do another check 6 against 8 and so on. So no matter where I am the difference will be always 2. If the user never reaches a difference, lets say stops at a 9 then the difference will be 1 and of course then nothing will happen. – Asperger Sep 17 '15 at 21:22
  • @Asperger but most of the answers don't set previous except at 100. You would need to use `setTimeout` to do what you just described – charlietfl Sep 17 '15 at 21:25
  • @charlietfl what makes me wonder. We have the variable previousWidth inside the onResize so the update happen every time. Ok I get the issue but setTimeout would be hard to control or lets say to efficient as it is based on a delay which might not be accurate. Correct me if im wrong – Asperger Sep 17 '15 at 21:30
  • @Asperger depends on expectations. Very common to use `setTimeout` and `clearTimeout` to throttle resize since it happens many many times a second – charlietfl Sep 17 '15 at 21:34
  • 1
    @charlietfl oh yes i store settimout in a variable and then clear it. Seems reasonable to me.Might as well be twice as resource hungry, im not sure. At the other side no one keeps resizing infinitely – Asperger Sep 17 '15 at 21:35
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/89968/discussion-between-asperger-and-charlietfl). – Asperger Sep 17 '15 at 21:38
  • @Asperger - See the second edit for a refactor of this approach and a more structured implementation. – Travis J Sep 17 '15 at 22:13
1

Store the current width as oldWidth. In resize listener check if current width differs form oldWidth by 100px (or more). If it does, then do some stuff and update the oldWidth with current value.

var oldWidth = window.innerWidth;
var onResize = function () {
var w = window.innerWidth;

    if ( Math.abs( w - oldWidth ) >= 100 ) {
    // then do some stuff
      console.log( 'diff >= 100px' );
      oldWidth = w;
    }
}

window.addEventListener('resize', onResize);

http://jsfiddle.net/xtx1L6nr/

pawel
  • 35,827
  • 7
  • 56
  • 53
  • Wow I was not even close to this – Asperger Sep 17 '15 at 20:51
  • I will check this out and I might have to ask you a few things after – Asperger Sep 17 '15 at 20:52
  • @Asperger you were close, but you need to keep in mind that the chances of difference being exactly 100 are small, that's why you need to check if it's 100 or more. And the `Math.abs` is to account for positive and negative differences (users may scale the window up or down) – pawel Sep 17 '15 at 20:56
  • What about Math.Round or Math.floor? It rounds the numbers – Asperger Sep 17 '15 at 20:58
  • Also what do I need Math.abs for if I can just substruct? – Asperger Sep 17 '15 at 21:00
  • @Asperger `.round`/`.floor` would be of no use here because you'd need them to deal with fractions (e.g. to convert `105.256` to `105`). `Math.abs`, however, converts negative numbers to to positive, so if the difference is `-120` (user scaled the windows down by 120px) using `.abs` returns just `120` which is bigger than `100`. You could also write `if(w - oldWidth >= 100 || w - oldWidth <= 100)`. – pawel Sep 17 '15 at 21:07
  • I think I understand (I hope I do). Im wondering which of the solutions would be more elegant now – Asperger Sep 17 '15 at 21:12
0

Try the following. But be aware - "resize" events are sent periodically as the user resizes the window. This code will have the effect of detecting resizes that occur instantaneously (minimize, maximize) or very quickly - faster than 100px per render tick. If you want the eventual size of the final resize, once the user lets go, you'll need a timeout, like in the answer to this question: Detect when a window is resized using JavaScript ?

var previousWidth;
var detectBigChange = function () {
  var newWidth = window.innerWidth || document.body.clientWidth;
  if (!!previousWidth && Math.abs(newWidth - previousWidth) > 100) {
    console.log("Big change!");
  }
  previousWidth = newWidth;
}

window.addEventListener('resize', detectBigChange);
detectBigChange();
Community
  • 1
  • 1
Richard Peterson
  • 873
  • 6
  • 15
  • I ask you as well. Why do we use math.abs if we can use just "-" the minus operator? Or does it not work? – Asperger Sep 17 '15 at 21:01
  • Resizing larger will create positive numbers. Resizing smaller will create negative numbers. For instance, old width was 1000. New width is 800. newWidth - oldWidth will be -200. -200 is less than 100, so even though the resize was big, we don't get the right answer. Math.abs() will take the negative number and convert it to positive 200, which is larger than 100. So you'll get the answer you want. – Richard Peterson Sep 17 '15 at 23:57