Home > OS >  Stop bar animation with jQuery/javascript
Stop bar animation with jQuery/javascript

Time:04-19

Completely new to JS and jQuery here. The code iterates through each one of the bars and when you press the Reset button, it should shrink the bars down to zero. However, I am trying to get this bar graph animation to stop but once you press the button, it goes through the animation and resets back again to where it was before. Any help would be appreciated!

function barGraph(data) {

  //Create graph
  var graph = document.createElement("div");
  graph.id = "barGraph";

  //inserting bar graph before previous 'label' div
  const target = document.querySelector('#labels');
  target.parentNode.insertBefore(graph, labels);

  //Styling for graph
  graph.style.position = "relative";
  graph.style.marginTop = "20px";
  graph.style.height = "500px";
  graph.style.backgroundColor = "Gainsboro";
  graph.style.borderBottomStyle = "solid";
  graph.style.borderBottomWidth = "1px";
  graph.style.overflow = "hidden";

  //Iterating through each bar
  var position = 50;
  var width = 75;
  for (i = 0; i < data.length; i  = 1) {
    var spendData = data[i];
    var bar = document.createElement("div");
    //set a unique id for each of the bars
    bar.id = data[i].category;

    //Styling for bar
    bar.style.position = "absolute";
    bar.style.left = position   "px";
    bar.style.width = width   "px";
    bar.style.backgroundColor = spendData.color;
    bar.style.height = (spendData.amount) / 5   "px";
    bar.style.bottom = "0px";
    bar.innerHTML = "$"   spendData.amount;
    bar.style.fontSize = "11px";
    bar.style.color = "Azure";
    bar.style.fontWeight = "800";
    bar.style.fontFamily = "Arial, Helvetica, sans-serif";
    bar.style.textAlign = "center";
    bar.style.padding = "1em";

    //Appending to the graph
    graph.appendChild(bar);

    //Set bar width
    position  = (width * 2);
  }

  return graph;
}

window.onload = function() {
  var data = [{
      category: "Food and Dining",
      amount: "2005.00",
      color: "CadetBlue"
    },
    {
      category: "Auto and Transport",
      amount: "1471.31",
      color: "CornflowerBlue"
    },
    {
      category: "Shopping",
      amount: "892.86",
      color: "DarkCyan"
    },
    {
      category: "Bills and Utilities",
      amount: "531.60",
      color: "DarkSeaGreen"
    },
    {
      category: "Mortgage",
      amount: "1646.00",
      color: "LightSeaGreen"
    },
    {
      category: "Entertainment",
      amount: "179.52",
      color: "YellowGreen"
    }
  ];

  document.getElementById("resetGraph").addEventListener("click", function() {
    for (i = 0; i < data.length; i  ) {
      var bar = document.getElementById(data[i].category);
      if (bar) {
        bar.animate({
          "height": "0px",
          "padding": "0px"
        }, 2000);
      }
    }
  });

  var graph = barGraph(data);
  //document.div.appendChild(graph);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="labels"></div>
<button id="resetGraph">Reset graph</button>
<script src="js/spending-graph.js"></script>

CodePudding user response:

Dom's animate() function will return an AnimationPlaybackEvent object. And we can use onFinish method to reset the height and width of the dom element or can hide it.

Can you please add following lines to your code after bar.animate() function.

.onfinish=function(e){
        e.currentTarget.effect.target.style.visibility ="hidden"
    };

like below snippet.

bar.animate({
      "height": "0px",
      "padding": "0px"
    }, 2000).onfinish=function(e){
        e.currentTarget.effect.target.style.visibility ="hidden"
    };

CodePudding user response:

Updated

Interesting question! AFAICT the problem is that you've mixed up jQuery's .animate() with the native Javascript .animate().

Your question is tagged jQuery-animate, you mention it explicitly, and the format of the parameter you are passing to .animate() is exactly what jQuery expects.

But your animate code is running against an HTML DOM element, not a jQuery element:

// bar here is an HTML DOM Element, not a jQuery object:
var bar = document.getElementById(data[i].category);

// That means this will run JS .animate, not jQuery's:
bar.animate( ... );

If you simply change that to run against a jQuery object, it works (almost) as expected:

var bar = $('#'   data[i].category);
bar.animate( ... );

I say almost because there is another problem:

bar.id = data[i].category;

This creates HTML IDs from your plain English categories, which include whitespace, like "Food and Dining". According to the spec:

id's value must not contain whitespace (spaces, tabs etc.).

And at least when trying to use IDs with spaces as jQuery selectors, this matters, and it does not work. So instead let's just use the array index, which is also guaranteed to be unique, with a prefix so we know what we're referring to:

bar.id = 'bar-'   i;

Here's a working snippet with those 2 changes.

Note: the last document.div.appendChild(graph); was throwing an error - maybe this was a debugging attempt, it isn't necessary and I've commented it out here.

function barGraph(data) {

  //Create graph
  var graph = document.createElement("div");
  graph.id = "barGraph";

  //inserting bar graph before previous 'label' div
  const target = document.querySelector('#labels');
  target.parentNode.insertBefore(graph, labels);

  //Styling for graph
  graph.style.position = "relative";
  graph.style.marginTop = "20px";
  graph.style.height = "500px";
  graph.style.backgroundColor = "Gainsboro";
  graph.style.borderBottomStyle = "solid";
  graph.style.borderBottomWidth = "1px";
  graph.style.overflow = "hidden";

  //Iterating through each bar
  var position = 50;
  var width = 75;
  for (i = 0; i < data.length; i  = 1) {
    var spendData = data[i];
    var bar = document.createElement("div");
    //set a unique id for each of the bars
    // UPDATED - category includes spaces, just use array index
    // bar.id = data[i].category;
    bar.id = 'bar-'   i;

    //Styling for bar
    bar.style.position = "absolute";
    bar.style.left = position   "px";
    bar.style.width = width   "px";
    bar.style.backgroundColor = spendData.color;
    bar.style.height = (spendData.amount) / 5   "px";
    bar.style.bottom = "0px";
    bar.innerHTML = "$"   spendData.amount;
    bar.style.fontSize = "11px";
    bar.style.color = "Azure";
    bar.style.fontWeight = "800";
    bar.style.fontFamily = "Arial, Helvetica, sans-serif";
    bar.style.textAlign = "center";
    bar.style.padding = "1em";

    //Appending to the graph
    graph.appendChild(bar);

    //Set bar width
    position  = (width * 2);
  }

  return graph;
}

window.onload = function() {
  var data = [{
      category: "Food and Dining",
      amount: "2005.00",
      color: "CadetBlue"
    },
    {
      category: "Auto and Transport",
      amount: "1471.31",
      color: "CornflowerBlue"
    },
    {
      category: "Shopping",
      amount: "892.86",
      color: "DarkCyan"
    },
    {
      category: "Bills and Utilities",
      amount: "531.60",
      color: "DarkSeaGreen"
    },
    {
      category: "Mortgage",
      amount: "1646.00",
      color: "LightSeaGreen"
    },
    {
      category: "Entertainment",
      amount: "179.52",
      color: "YellowGreen"
    }
  ];

  document.getElementById("resetGraph").addEventListener("click", function() {
    for (i = 0; i < data.length; i  ) {
      // UPDATED: 1. use new format ID without spaces
      // UPDATED: 2. use jQuery selector/animation
      // UPDATED: 3. use bar.length to test if selector matched anything
      var bar = $('#bar-'   i);
      if (bar.length) {
        bar.animate({
          "height": "0px",
          "padding": "0px"
        }, 2000);
      }
    }
  });

  var graph = barGraph(data);
  //document.div.appendChild(graph);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div id="labels"></div>
<button id="resetGraph">Reset graph</button>

So what is happening in your original code? I am not sure. It is the native Javascript .animate() that is running, not jQuery's. The first parameter of the Javascipt .animate() method should be:

Either an array of keyframe objects, or a keyframe object whose properties are arrays of values to iterate over.

Your code is passing an object, not an array, so .animate() will expect the latter, "a keyframe object". The Keyframe Formats spec gives a little more detail and some examples for what "a keyframe object" looks like:

element.animate({
  opacity: [ 0, 1 ],          // [ from, to ]
  color:   [ "#fff", "#000" ] // [ from, to ]
}, 2000);

Your object does not match this format though - each attribute is just a string, not an array of start/stop values. The Keyframe docs also describe Implicit to/from keyframes:

In newer browser versions, you are able to set a beginning or end state for an animation only (i.e. a single keyframe), and the browser will infer the other end of the animation if it is able to.

My guess is this is what is happening, somehow? The browser is inferring an implicit end frame of the start values?

I tried to update your code to use the right Keyframe format, so you can use the native .animate() and skip jQuery all together, but it does not work - the bars reset to their original height after the animation, just like in your original code, I don't know why:

document.getElementById("resetGraph").addEventListener("click", function() {
    var bar, startHeight;

    for (i = 0; i < data.length; i  ) {
        bar = document.getElementById('bar-'   i);
        if (bar) {
            startHeight = window.getComputedStyle(bar).height;
            bar.animate({
              height: [startHeight, '0px'],
              padding: ['1em', '0']
            }, 2000);
        }
    }
});
  • Related