2

Objective

  • I'm trying to iterate over items in an array using a for loop and use jQuery's .append() method to add a series of circles, which represent this particular player's home runs over their career, atop four svgs of a baseball stadium, in order to create a spray chart of hits.

Codepen:

https://codepen.io/onlyandrewn/project/editor/DkaEvW

Problem

The correct number of circles are not being appended to the four svgs, which I expect to be 48 red dots (career home runs) and 22 red dots (this past season).

What I've tried

  • On lines 18-21: Within the for loop, I've tried using jQuery to target each of the individual svgs based on their classes (.layer_1, .layer_2 etc.), but it appears that the total number of dots are being divided over the four svgs, instead of having all 48 dots (career home runs) or all 22 (season home runs) show up on one chart
  • On lines 24-31: I tried to show just data[i].game_year using an if-statement in order to get the home runs from this past 2017 season, but the dots that were being displayed were not from this past year.
  • Also tried, switching the classes to ids and using each and forEach to try to target the parent svgs individually.

scripts.js

$(function(){

    // Make a GET request with an AJAX call to fetch data from the json file, which contains stats on Marcell Ozuna's career homeruns from MLB Statcast via BaseballSavant.com

    // Documentation: http://api.jquery.com/jquery.ajax/
    // Example GET request: https://stackoverflow.com/questions/9269265/ajax-jquery-simple-get-request

    $.ajax({
        url: 'ozuna.json',
        method: 'GET',
        dataType: "json",
    }).then(function(data){

        for (i = 0; i < data.length; i++) {

            // On each of the SVG's of the baseball stadiums, use a for loop to iterate over each of the 48 items in the array. Then, append a circle with a class of `homerun` to the div.

            $(".layer_1").append("<svg><circle class='homerun'></svg>");
            $(".layer_2").append("<svg><circle class='homerun'></svg>");
            $(".layer_3").append("<svg><circle class='homerun'></svg>");
            $(".layer_4").append("<svg><circle class='homerun'></svg>");

            // Marcell Ozuna hit a total of 22 homeruns last season
            // if (data[i].game_year === 2017) {
            //  // console.log(data[i].game_year);
            //  // $(".layer_2").append("<svg><circle class='homerun'></svg>");
            //  // $(".layer_3").append("<svg><circle class='homerun'></svg>");
            // } else {
            //  // $(".layer_1").append("<svg><circle class='homerun'></svg>");
            //  // $(".layer_4").append("<svg><circle class='homerun'></svg>");
            // }

            // This refers to each of the circles. Each of the circles contains the following attributes. The user sees a small red dot with a black border.
            $(".homerun").each(function(index){
                $(this).attr({
                    cx: data[index].hc_x,
                    cy: data[index].hc_y, 
                    r: 4.71,
                    fill: "#a82254",
                    stroke: "#000",
                    "stroke-width": 1, 
                })

                $(this).hover(function(){
                    changeText(data, index);
                    showSidebar();
                })
            });
        }
    });

    // When you hover over one of the circles, change the text in the sidebar. It takes two parameters, the data from the AJAX call and the index of the object in the array.
    function changeText(data, index) {
        $(".sidebar__date").html(data[index].game_date);
        $(".team--home").html(data[index].home_team);
        $(".team--away").html(data[index].away_team);
        $(".sidebar__tb").html(data[index].inning_topbot);
        $(".sidebar__inning").html(data[index].inning);
        $(".sidebar__description").html(data[index].des_long);
        $(".sidebar__pitch").html(data[index].pitch_type);
        $(".value--balls").html(data[index].balls);
        $(".value--strikes").html(data[index].strikes);
        $(".value--outs").html(data[index].outs_when_up);
    }

    // Show the game details. By default, the sidebar is empty and a user will see only the attribution until they hover over the first red dot.
    function showSidebar() {
        $(".sidebar").show();
    }
});

Example of json data

[
  {
    "pitch_type": "four-seam fastball",
    "release_speed": 92.4,
    "game_date": "March 31, 2014",
    "game_year": 2014,
    "des_long": "In the bottom of the 3rd, Marcell Ozuna homers off a 92.4 mph four-seam fastball to left field. A. J. Ellis scores. Christian Yelich scores. Giancarlo Stanton scores.",
    "des_short": "Marcell Ozuna homers on a line drive to left field.",
    "home_team": "MIA",
    "away_team": "COL",
    "balls": 3,
    "strikes": 1,
    "outs_when_up": 0,
    "inning_topbot": "bottom",
    "inning": "3rd",
    "hc_x": 19.08,
    "hc_y": 85.34,
    "hit_distance_sc": "null"
  },
Andrew Nguyen
  • 1,416
  • 4
  • 21
  • 43

2 Answers2

3

Your approach is a bit wrong.

Firstly you don't need to create an SVG for every circle. Just add circles to the SVGs. For each ballpark image, loop through the data, add a circle if it's the right year.

    $.ajax({
        url: 'ozuna.json',
        method: 'GET',
        dataType: "json",
    }).then(function(data){

    // Marlins Park
    addHomeRuns(data, '.layer_1', 'all');
    addHomeRuns(data, '.layer_2', 2017);
    // Busch
    addHomeRuns(data, '.layer_3', 'all');
    addHomeRuns(data, '.layer_4', 2017);

    });


  // On each of the SVG's of the baseball stadiums, use a for loop to iterate over each of the 48 items in the array. Then, append a circle with a class of `homerun` to the div.
  function addHomeRuns(data, layerClass, year)
  {
    var svg = $(layerClass).get(0);

    $.each(data, function(i, obj) {

      var showHomer = year === 'all' || obj.game_year === year;
      if (showHomer)
      {
        var circle = document.createElementNS(svg.namespaceURI, 'circle');
        circle.setAttribute('cx', obj.hc_x);
        circle.setAttribute('cy', obj.hc_y);
        circle.setAttribute('r', 4.71);
        circle.setAttribute('fill', "#a82254");
        circle.setAttribute('stroke', "#000");
        circle.setAttribute('stroke-width', 1);
        svg.appendChild(circle);
      }

      $(circle).hover(function() {
        changeText(obj);
        showSidebar();
      });
    });
  }

Updated Codepen here

Paul LeBeau
  • 97,474
  • 9
  • 154
  • 181
1

You are using classname layer_1 in javascript and capitalized Layer_1 in html. Since jQuery selectors are case sensitive, this could be the main issue.

bigless
  • 2,849
  • 19
  • 31
  • Tried it your suggestion, but unfortunately not the root the problem. What is happening now is closer to what I describe in my question, where it appears that the total number of circles is being divided amongst the four svgs. – Andrew Nguyen Jan 11 '18 at 00:24
  • 1
    @Andrew Nguyen I see there many mistakes. For example there is `data[index]` which means that you dont get current element of array but rather element of array based on index of current DOM element.. – bigless Jan 11 '18 at 00:38
  • @AndrewNguyen Another thing is that you are iterating all elements with class homerun in document. There is more and more and you still rewrites their attr.. – bigless Jan 11 '18 at 00:42
  • Do you have a code snippet that you would like to share? – Andrew Nguyen Jan 11 '18 at 03:05
  • I have not time to rewrite your code, but somebody already did it bellow. Also I think that point out issues and let you solve it is better for your understanding than serving complete code.. – bigless Jan 11 '18 at 10:15