3
\$\begingroup\$

Recently I built a map with custom markers (pins) and a sorting function to display each by type (Member, Supplier, Prospect, New Member).

The HTML (check boxes wired up with sort() function):

<input type="checkbox" value="Supplier" onclick="sort()" id="switch2" />
          <label for="switch2">Suppliers</label>

The JavaScript:

var markersArray = [];

// data schema  ['Company Name',"Address",Latitude,Longitude,"Type"]
var data = [
  ['Company A',"Address",Latitude,Longitude,"Member"],
  ['Company B',"Address",Latitude,Longitude,"Supplier"],
  ['Company C',"Address",Latitude,Longitude,"Prospect"],
];

function initialize(data) {

  var mapOptions = {
    zoom: 3,
    center: new google.maps.LatLng(40, -90)
  }
  var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);

  // Create the legend with content
  var legend = document.createElement('div');
      legend.id = 'legend';

  var content = [];
      content.push('<h3>Legend</h3>');
      content.push('<p><div class="legIcon"> <img src="pin_blue.png" alt="icon color" /> </div>Members</p>');
      content.push('<p><div class="legIcon"> <img src="pin_red.png" alt="icon color" /> </div>Suppliers</p>');
      content.push('<p><div class="legIcon"> <img src="pin_purple.png" alt="icon color" /> </div>New Members</p>');
      content.push('<p><div class="legIcon"> <img src="pin_green.png" alt="icon color" /> </div>Prospects</p>');

  legend.innerHTML = content.join('');
  legend.index = 1;

  // call the legend 
  map.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(legend);

  setMarkers(map, data);

}


function setMarkers(map, locations) {

  if (locations == undefined) {
    locations = data;
    return initialize(locations);
  }


  for (var i = locations.length - 1; i >= 0; i--) {
      var company = locations[i],
          pinColor = '';

      var myLatLng = new google.maps.LatLng(company[2], company[3]);
      var marker = new google.maps.Marker({
          position: myLatLng,
          map: map,
          icon: image,
          shape: shape,
          title: company[0],
          type: company[4]
      });

      markersArray.push(marker);

      if ( marker.type === 'Member' ) { pinColor = 'blue' }
      else if ( marker.type === 'New') { pinColor = 'purple' }
      else if ( marker.type === 'Supplier') { pinColor = 'red' }
      else { pinColor = 'green' }  

      var image = {
        url: 'pin_' + pinColor + '.png',
        size: new google.maps.Size(100, 100),
        origin: new google.maps.Point(0,0),
        anchor: new google.maps.Point(20, 40)
      };
  };


}

// initialize map with empty array
google.maps.event.addDomListener(window, 'load', function (data) { initialize([]) });

// standalone functions
function clearOverlays () {
  for (var i = 0; i < markersArray.length; i++) {
     markersArray[i].setMap(null);
  } 
  markersArray.length = 0;
}

function resetPins () {
  clearOverlays();
}

// sort and collect with sliding buttons (checkboxes)
function sort () {

  var newData = [];

  // Pass the checkbox name to the function
  function getCheckedBoxes(chkboxName) {
    var checkboxes = document.getElementsByName(chkboxName);
    var checkboxesChecked = [];
    // loop over them all
    for (var i=0; i<checkboxes.length; i++) {
       // And stick the checked ones onto an array...
       if (checkboxes[i].checked) {
          checkboxesChecked.push(checkboxes[i].value);
       } 
       // clear the unchecked
       else {
          clearOverlays();
       }
    }
    // Return the array if it is non-empty, or null
    return checkboxesChecked.length > 0 ? checkboxesChecked : null;
  }
  // call the boxes
  var checkedBoxes = getCheckedBoxes("switch");

  for (var i = 0; i < checkedBoxes.length; i++) {
    data.forEach(function (d) {
      if ( d[4] === checkedBoxes[i] ) {
          newData.push(d);
        };
    });
  }

  if (newData.length > 0) { 
    return initialize(newData); 
  }
  else { 
    return initialize(data); 
  }  

}

I have not written a ton of vanilla JavaScript, so I would like some advice on how to clean this up. The obvious: some loops count down, other up, there are various if/else statements that I feel may be superfluous, using .forEach inside a for loop.

Curiously, when the code runs, it skips the first element of data when assigning the custom pin as .png. Why does this code not hit the first element in the array?

\$\endgroup\$
4
\$\begingroup\$

Let's be frank - this code is a mess.

  • A bunch of global variables and functions. That's never good. You should at least wrap these inside a container function. Or better - turn it into an object.

  • In the middle of the function definitions there's suddenly call to google.maps.event.addDomListener(). Don't mix the definitions and code that does something. Bring all the initialization into one area.

  • You knew already that the backwards for-loop is troublesome. You should have fixed it before submitting. Don't submit code that you know is poor.

  • But when I look at the end of this for-loop... WTF? You are creating an image object and then you just throw it away. Is this code unfinished?

  • For some reason the getCheckedBoxes function is defined inside sort function. Why?

  • Inside clearOverlays you reset the markersArray by doing markersArray.length = 0. That's not a proper way to reset an array. Instead just assign an empty array to replace the old one: markersArray = [].

  • resetPins is just an alias to clearOverlays. Besides it's not used. Throw away the dead code.

  • The sort function doesn't seem to be doing any sorting. Don't call it sort if it just switches groups of markers on and off.

  • Too many other issues to mention...

Most importantly, try to care about your code. If you put your code out for others to see (by posting it here), try to first make it as good as you possibly can. Remove dead code. Fix the problems that you know about. Post a complete example, not half-finished one. Structure it so that it's as easy to read and understand as possible. Run it through JSHint and fix all the warnings, so you that people will not need to point out the mistakes that a stupid machine could have told you about.

\$\endgroup\$
  • \$\begingroup\$ Thanks Rene. Yes, it is unfinished, I am refactoring now and appreciate your guidance on improving the code. "Or better - turn it into an object." How would this look? \$\endgroup\$ – DeBraid Apr 5 '14 at 16:14
  • \$\begingroup\$ For Loops: I left both styles in to evoke discussion on which is most suitable. After turning to this: stackoverflow.com/questions/1340589/… its clear there is no clear answer. \$\endgroup\$ – DeBraid Apr 5 '14 at 16:23
  • 1
    \$\begingroup\$ By turning it into an object I meant writing it all in object-oriented style. You would basically turn it into a class, though as there are no real classes in JavaScript, that would mean creating a constructor function and adding the other functions as methods in its prototype. More on this topic: Introduction to Object-Oriented JavaScript \$\endgroup\$ – Rene Saarsoo Apr 5 '14 at 17:14
  • 1
    \$\begingroup\$ Regarding the backwards loop: (1) Only optimize when there's an actual bottleneck, otherwise strive for most readable code; (2) don't rely on micro-benchmarks, measure the difference with your own actual code. Most likely this code doesn't really have to be blazing fast, so you're much better off using Array.forEach() and other methods that better convey the intention of your code. \$\endgroup\$ – Rene Saarsoo Apr 5 '14 at 17:28
  • \$\begingroup\$ "You are creating an image object and then you just throw it away." Could you elaborate on this? \$\endgroup\$ – DeBraid Apr 6 '14 at 17:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.