Skip to content

Instantly share code, notes, and snippets.

@AloofBuddha
Created February 1, 2017 18:36
Show Gist options
  • Save AloofBuddha/2c9e877196a9f4f72da35c3fe2a0d031 to your computer and use it in GitHub Desktop.
Save AloofBuddha/2c9e877196a9f4f72da35c3fe2a0d031 to your computer and use it in GitHub Desktop.

Front-End JS Concepts

Front-end JS is different than Node for a number of reasons that can be confusing. Hopefully the info here will help you clear up some of the confusion you may have!

Important Note Upfront All of the concepts described here still apply in the modern day, but recent tools and trends have made some less relevant than others. All of this information still applies when sending over a plain HTML file, but a specific dev tool called Webpack solves a number of these problems and has as a result become a pretty essential tool for front-end development. I will try to make a note when it applies!

Including your css/js files

When we send back an HTML file as the response to a GET request, we can include any number of other resources using their URLs. This means using <script> (javascript), <link> (css) and even <img> tags with a src/href attribute whose value is a URL. The URL can be pointing to a resource on your own server or someone else's - either way it is a unique request for an external resource that may succeed or fail!

HTML is rendered in order (top to bottom) so the order we add our <link> and <script> tags matters. As a rule of thumb, we add <link> tags in our head, with third-party stylesheets first, and our first-party stylesheets second. The reason for this is that your first-party css may define a rule that you want to overwrite whatever the third-party stylsheets defined. If you did it in the opposite order, any of your own CSS rules that conflicted with (for example) Bootstrap's rules would be overwritten, and we want our rules to take precedence.

For <script> tags we add them at the bottom of the body. We do this so that our page can render all the HTML + CSS first, and only then do we fetch the logic (JS). This means the user can see the page faster, and the JS files will continue to be fetched and loaded as they browse. The difference should ideally be a few seconds, but whenever possible we want our page to function minimally with just HTML and CSS. Order matters here as well - every script may add some variable to the global scope, so if a later script requires that variable (i.e. depends on jQuery, expects to find the variable '$') it will only run correctly if the dependency has been fetched before it.

Example

<!DOCTYPE html>
<html>
  <!-- This name appears in the tab -->
  <title>Page Title</title>
  <!-- head is not rendered, but can include important metadata and can fetch assets -->
  <head>
    <!-- we get the bootstrap css file first -->
    <link href="/bootstrap/css/bootstrap.min.css" rel="stylesheet">
    <!-- and then our own - allowing us to overwrite a given rule bootstrap already defined -->
    <link href="/stylesheets/style.css" rel="stylesheet">
  </head>
  <!-- the body is where the HTML you expect to be rendered goes -->
  <body>
    <h1>A cool HTML page</h1>
    <!-- ... more HTML ... -->
    <footer>The end of our page</footer>
      <!-- we add our scripts at the very bottom so they are loaded last -->
       <!-- jQuery is added first as even third-party scripts depend on it -->
      <script src="/jquery/jquery.min.js"></script>
      <!-- for example, bootstrap.js! -->
      <script src="/bootstrap/js/bootstrap.min.js"></script>
      <!-- then we add our own in the order we expect them to be run -->
      <script src="/js/main.js"></script>
  </body>
</html>

You might be thinking it's annoying (not to mention slow) to have to include each script seperately and in the correct order to make your code function properly. In fact, in large projects, keeping track of dependencies this way can get very frustrating! Webpack fixes this by allowing you to specify dependencies with require like you do in Node and outputs a single file .js that is equivalent to the individual scripts.

Module & IIFE's

Def: IIFE - an Immediately Invoke Function Expression, aka a function we create and immediately call.

Example

var a = (function myFn() { 
  var b = 1; 
  console.log(b);

  return 2;
})();

Why?

Mainly to not 'pollute' the global scope. This hides the data we want to hide and also keeps us from overwriting variables other code may have declared. In the above example we declare var b = 1 but it stops existing after the function is called - we limit its scope to that function, which is only ever called once!

For anything we do want in the global scope, we can return it from the IIFE and set it to a variable. This is called the module pattern and is an example of the **

Here is a more realistic example:

Example

// our IIFE returns an object we can now access globally in others files
var mapModule = (function () { 
    // local variables are function-scoped, so won't pollute the global scope
   var center = new google.maps.LatLng(40.705086, -74.009151);

   var currentMap;
   
   var iconURLs = {
      hotel: '/images/lodging_0star.png',
      restaurant: '/images/restaurant.png',
      activity: '/images/star-3.png'
    };

   // anything that explicitly relies on the DOM should be wrapping in an
   // 'onReady' to be safe
   $(function () {
      var mapCanvas = document.getElementById('map-canvas');

      currentMap = new google.maps.Map(mapCanvas, {
        center: fullstackAcademy,
        zoom: 13,
        mapTypeId: google.maps.MapTypeId.ROADMAP,
        styles: styleArr
      });
   });


  // set up some initial data
  drawMarker('hotel', [40.705137, -74.007624]);
  drawMarker('restaurant', [40.705137, -74.013940]);
  drawMarker('activity', [40.716291, -73.995315]);

  // a function can be used both internally & externally
  function drawMarker (type, coords) {
    var latLng = new google.maps.LatLng(coords[0], coords[1]);
    var iconURL = iconURLs[type];
    var marker = new google.maps.Marker({
      icon: iconURL,
      position: latLng
    });

    marker.setMap(currentMap);
  }

  // our 'API' is what we want to expose as functionality. It should have use what other files need to interact with it and shouldn't expose any more than necessary
  return {
    getCurrentMap: function () {
      return currentMap;
    },

    drawMarker: drawMarker
  };

})();

When the script is added, this function is immediately executed, and any code in it is run. However, we also create a global variable called mapModule that will allow other scripts of ours to interact with this module in expected ways.

Why do we need to do this? Well, modules are an important tools for 'encapsulation', the idea that our code can hide implementation details and simply expose an interface through something like module.exports. So why don't we use module.exports and var module = require('./myModule')? It's because these are Node features and aren't natively supported in browsers. If you find this annoying, well you are in good company, and that's what Webpack is supposed to resolve, among other things. Webpack let's you use modules like you do on the backend and compiles your code together into one file, so dependency management can be handled a lot more easily.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment