How many named colors are there on the web? Well, that is a complicated question that can be answered in the continually updated Wikipedia article on Web Colors .

Not all browsers support all colors and while the effective number of colors has grown, the list of named CSS colors I found that almost all browsers supported was about 140… I have 138 below I believe. The are other standards with multiple numbered shades of given colors would add up to a greater number. Again, you can read about it all at Wikipedia.

Would you like an array of all named the CSS colors widely accepted on all browsers? Named colors with their both their hexadecimal and their RGB codes available by index ?

Here is one…free of charge…all typed up and grouped in a rough way. I have them displayed below that and you can hover for the color name.

Do you like guessing colors? Try a color game that I made right after BootCamp.

I hand formatted this array a couple years ago before I knew what I was doing. I probably would have made them objects if I had it to do again but, as Tim Gunn says, if you need something like this quickly you can “make it work”.

var colors = [
    ["maroon", "#800000", 128, 0, 0],
    ["darkred", "#8B0000", 139, 0, 0],
    ["firebrick", '#B22222', 178, 34, 34],
    ["crimson", "#DC143C", 220, 20, 60],
    ["red", "#FF0000", 255, 0, 0],
    ["tomato", "#FF6347", 255, 99, 71],
    ["coral", "#FF7F50", 255, 127, 80],
    ["indianred", "#CD5C5C", 205, 92, 92],
    ["lightcoral", "#F08080", 240, 128, 128],
    ["darksalmon", "#E9967A", 233, 150, 122],
    ["salmon", "#FA8072", 250, 128, 114],
    ["lightsalmon", "#FFA07A", 255, 160, 122],
    ["bisque", "#FFE4C4", 255, 228, 196],
    ["blanchedalmond", "#FFEBCD", 255, 235, 205],
    ["peachpuff", "#FFDAB9", 255, 218, 185],
    ["wheat", "#F5DEB3", 245, 222, 179],
    ["navajowhite", "#FFDEAD", 255, 222, 173],
    ["cornsilk", "#FFF8DC", 255, 248, 220],
    ["saddlebrown", "#8B4513", 139, 69, 19],
    ["sienna", "#A0522D", 160, 82, 45],
    ["orangered", "#FF4500", 255, 69, 0],
    ["darkorange", "#FF8C00", 255, 140, 0],
    ["orange", "#FFA500", 255, 165, 0],
    ["brown", "#A52A2A", 165, 42, 42],
    ["gold", "#FFD700", 255, 215, 0],
    ["darkgoldenrod", "#B8860B", 184, 134, 11],
    ["goldenrod", "#DAA520", 218, 165, 32],
    ["palegoldenrod", "#EEE8AA", 238, 232, 170],
    ["darkkhaki", "#BDB76B", 189, 183, 107],
    ["khaki", "#F0E68C", 240, 230, 140],
    ["moccasin", "#FFE4B5", 255, 228, 181],
    ["antiquewhite", "#FAEBD7", 250, 235, 215],
    ["beige", "#F5F5DC", 245, 245, 220],
    ["lemonchiffon", "#FFFACD", 255, 250, 205],
    ["lightgoldenrodyellow", "#FAFAD2", 250, 250, 210],
    ["lightyellow", "#FFFFE0", 255, 255, 224],
    ["chocolate", "#D2691E", 210, 105, 30],
    ["peru", "#CD853F", 205, 133, 63],
    ["sandybrown", "#F4A460", 244, 164, 96],
    ["burlywood", "#DEB887", 222, 184, 135],
    ["tan", "#D2B48C", 210, 180, 140],
    ["rosybrown", "#BC8F8F", 188, 143, 143],
    ["yellow", "#FFFF00", 255, 255, 0],
    ["olive", "#808000", 128, 128, 0],
    ["darkolivegreen", "#556B2F", 85, 107, 47],
    ["olivedrab", "#6B8E23", 107, 142, 35],
    ["lawngreen", "#7CFC00", 124, 252, 0],
    ["chartreuse", "#7FFF00", 127, 255, 0],
    ["greenyellow", "#ADFF2F", 173, 255, 47],
    ["darkgreen", "#006400", 0, 100, 0],
    ["green", "#008000", 0, 128, 0],
    ["forestgreen", "#228B22", 34, 139, 34],
    ["lime", "#00FF00", 0, 255, 0],
    ["lightcyan", "#E0FFFF", 224, 255, 255],
    ["limegreen", "#32CD32", 50, 205, 50],
    ["lightgreen", "#90EE90", 144, 238, 144],
    ["palegreen", "#98FB98", 152, 251, 152],
    ["darkseagreen", "#8FBC8F", 143, 188, 143],
    ["mediumspringgreen", "#00FA9A", 0, 250, 154],
    ["springgreen", "#00FF7F", 0, 255, 127],
    ["yellowgreen", "#9ACD32", 154, 205, 50],
    ["seagreen", "#2E8B57", 46, 139, 87],
    ["mediumaquamarine", "#66CDAA", 102, 205, 170],
    ["mediumseagreen", "#3CB371", 60, 179, 113],
    ["lightseagreen", "#20B2AA", 32, 178, 170],
    ["darkslategray", "#2F4F4F", 47, 79, 79],
    ["teal", "#008080", 0, 128, 128],
    ["darkcyan", "#008B8B", 0, 139, 139],
    ["aqua", "#00FFFF", 0, 255, 255],
    ["cyan", "#00FFFF", 0, 255, 255],
    ["darkturquoise", "#00CED1", 0, 206, 209],
    ["turquoise", "#40E0D0", 64, 224, 208],
    ["mediumturquoise", "#48D1CC", 72, 209, 204],
    ["paleturquoise", "#AFEEEE", 175, 238, 238],
    ["aquamarine", "#7FFFD4", 127, 255, 212],
    ["powderblue", "#B0E0E6", 176, 224, 230],
    ["cadetblue", "#5F9EA0", 95, 158, 160],
    ["steelblue", '#4682B4', 70, 130, 180],
    ['cornflowerblue', "#6495ED", 100, 149, 237],
    ["deepskyblue", "#00BFFF", 0, 191, 255],
    ["dodgerblue", "#1E90FF", 30, 144, 255],
    ["lightblue", "#ADD8E6", 173, 216, 230],
    ["skyblue", "#87CEEB", 135, 206, 235],
    ["lightskyblue", "#87CEFA", 135, 206, 250],
    ["midnightblue", "#191970", 25, 25, 112],
    ["navy", "#000080", 0, 0, 128],
    ["darkblue", "#00008B", 0, 0, 139],
    ["mediumblue", "#0000CD", 0, 0, 205],
    ["blue", "#0000FF", 0, 0, 255],
    ["royalblue", "#4169E1", 65, 105, 225],
    ["blueviolet", "#8A2BE2", 138, 43, 226],
    ["indigo", "#4B0082", 75, 0, 130],
    ["darkslateblue", "#483D8B", 72, 61, 139],
    ["slateblue", "#6A5ACD", 106, 90, 205],
    ["mediumslateblue", "#7B68EE", 123, 104, 238],
    ["mediumpurple", "#9370DB", 147, 112, 219],
    ["darkmagenta", "#8B008B", 139, 0, 139],
    ["darkviolet", "#9400D3", 148, 0, 211],
    ["darkorchid", "#9932CC", 153, 50, 204],
    ["mediumorchid", "#BA55D3", 186, 85, 211],
    ["purple", "#800080", 128, 0, 128],
    ["thistle", "#D8BFD8", 216, 191, 216],
    ["plum", "#DDA0DD", 221, 160, 221],
    ["violet", "#EE82EE", 238, 130, 238],
    ["magenta", "#FF00FF", 255, 0, 255],
    ["orchid", "#DA70D6", 218, 112, 214],
    ["mediumvioletred", "#C71585", 199, 21, 133],
    ["palevioletred", "#DB7093", 219, 112, 147],
    ["deeppink", "#FF1493", 255, 20, 147],
    ["hotpink", "#FF69B4", 255, 105, 180],
    ["lightpink", "#FFB6C1", 255, 182, 193],
    ["pink", "#FFC0CB", 255, 192, 203],
    ["mistyrose", "#FFE4E1", 255, 228, 225],
    ["lavenderblush", "#FFF0F5", 255, 240, 245],
    ["linen", "#FAF0E6", 250, 240, 230],
    ["oldlace", "#FDF5E6", 253, 245, 230],
    ["papayawhip", "#FFEFD5", 255, 239, 213],
    ["seashell", "#FFF5EE", 255, 245, 238],
    ["mintcream", "#F5FFFA", 245, 255, 250],
    ["slategray", "#708090", 112, 128, 144],
    ["lightslategray", "#778899", 119, 136, 153],
    ["lightsteelblue", "#B0C4DE", 176, 196, 222],
    ["lavender", "#E6E6FA", 230, 230, 250],
    ["floralwhite", "#FFFAF0", 255, 250, 240],
    ["aliceblue", "#F0F8FF", 240, 248, 255],
    ["ghostwhite", "#F8F8FF", 248, 248, 255],
    ["honeydew", "#F0FFF0", 240, 255, 240],
    ["ivory", "#FFFFF0", 255, 255, 240],
    ["azure", "#F0FFFF", 240, 255, 255],
    ["snow", "#FFFAFA", 255, 250, 250],
    ["black", "#000000", 0, 0, 0],
    ["dimgray", "#696969", 105, 105, 105],
    ["gray", "#808080", 128, 128, 128],
    ["darkgray", "#A9A9A9", 169, 169, 169],
    ["silver", "#C0C0C0", 192, 192, 192],
    ["lightgray", "#D3D3D3", 211, 211, 211],
    ["gainsboro", "#DCDCDC", 220, 220, 220],
    ["whitesmoke", "#F5F5F5", 245, 245, 245],
    ["white", "#FFFFFF", 255, 255, 255]
];




I decided to take a look back at AngularJS this week after having spent the last few months with other front-end DSL environments and platforms. (EmberJS and, to the extent it qualifies, Jekyll/Liquid).

I think the time I spent learning EmberJS late in 2016 really began to get me in the mind-set of web components. More on EmberJS in other posts, and I’ll leave the “meta” discussion of web components up to the trend setters…I’m following you though!

AngularJS has been actively moving toward components itself, even in Angular 1.X which has an aura of being ‘deprecated’.

As RTFM works, you can find out these things! What’s more, some precursors to components were there in angularJS all along.

What I’m Sharing:

  • a basic example of a directive that can modularize “sortable tables”
  • a few examples of AngularJS bells and whistles in use
  • an example how you can pass different actions to the same reusable custom directive you create.

sortable table demo

Really it is that last one, the passing of a function name to a directive, enabling the same directive to be used in different ways, that made me want to share this.

While I’m not sure I nailed the “AngularJS way”, the process had me taking a deeper dive.

The AngularJS website and style guides didn’t really have any great examples so, maybe seeing my syntax will get your juices flowing.

A quick reminder why to use directives

First, open up the (external) live demo to see the two sortable lists where we’ll reuse page segments that are more than flat information displays.

Second, open the code toggle below for something ugly!


 <div class="container" >
    <div class="col-md-12">
      <div ng-controller="RatingListController as list ">
      <div class="col-md-3 master-list">
        <table id="all-performer-table" class="table table-bordered table-striped">
          <thead ng-init="sortType='score'; sortReverse=true ">
            <tr>
              <td>
                <a href="#"  ng-click="sortType = '-name'; sortReverse = !sortReverse">
                  Select From Below
                  <span ng-show="sortType == '-name' && sortReverse" class="fa fa-caret-down"></span>
                  <span ng-show="sortType == '-name' && !sortReverse" class="fa fa-caret-up"></span>
                </a>
              </td>
              <td>
                <a href="#" ng-click="sortType = 'score'; sortReverse = !sortReverse">
                Score 
                  <span ng-show="sortType == 'score' && !sortReverse" class="fa fa-caret-down"></span>
                  <span ng-show="sortType == 'score' && sortReverse" class="fa fa-caret-up"></span>
                </a>
              </td>
              <td class="check-box-label">  
               +<span style="color: red;"> -</span>
              </td>
            </tr>
          </thead>  
          <tr ng-repeat="item in list.itemsInSet | orderBy:sortType:sortReverse">
            <td ng-class="{limeHighlight: item.selected}" >{{item.name}}</td>
            <td>{{item.score}}</td>
            <td><input type="checkbox" ng-model=item.selected ng-click="list.addOrRemoveFromRatedItems(item)"></td>
          </tr>
        </table>
      </div>
      <div class="col-md-4 selected-list">
        <ol class="selected-item-list">
          <li ng-repeat="selItem in list.ratedItems" class="selected-item-rated">
            <span >{{selItem.name}}- {{selItem.$$hashKey}}</span>
            <div class="list-making-tools pull-right" >
              <span class="glyphicon glyphicon-remove-circle text-danger" ng-click="list.removeFromRatedItems(selItem)"></span>
              <span class="glyphicon glyphicon-arrow-down text-warning " ng-click="list.moveDownRatedItems(selItem)"></span>
              <span class="glyphicon glyphicon-arrow-up text-success" ng-click="list.moveUpRatedItems(selItem)"></span>
            </div>
          </li>
        </ol>
      </div>
      </div> <!-- end RatingListController -->
      <div class="col-md-5 demo-explanation " ng-controller="PhotoDisplayController as photos">
        <div class="photo-box">
          <img ng-src="{{photos.chosenPhoto.photo_url}}" alt="{{photos.chosenPhoto.title}}">
        </div>
        <table id="photo-table" class="table table-bordered table-striped">
          <thead ng-init="sortType='score'; sortReverse=true ">
            <tr>
              <td>
                <a href="#"  ng-click="sortType = '-name'; sortReverse = !sortReverse">
                  Select From Below
                  <span ng-show="sortType == '-name' && sortReverse" class="fa fa-caret-down"></span>
                  <span ng-show="sortType == '-name' && !sortReverse" class="fa fa-caret-up"></span>
                </a>
              </td>
              <td>
                <a href="#" ng-click="sortType = 'score'; sortReverse = !sortReverse">
                Year 
                  <span ng-show="sortType == 'score' && !sortReverse" class="fa fa-caret-down"></span>
                  <span ng-show="sortType == 'score' && sortReverse" class="fa fa-caret-up"></span>
                </a>
              </td>
              <td class="check-box-label">  
               +<span style="color: red;"> -</span>
              </td>
            </tr>
          </thead>  
          <tr ng-repeat="item in photos.itemsInSet | orderBy:sortType:sortReverse">
            <td ng-class="{limeHighlight: item.selected}" >{{item.name}}</td>
            <td>{{item.year}}</td>
            <td><input type="checkbox" ng-model=item.selected ng-click="list.addOrRemoveFromRatedItems(item)"></td>
          </tr>
        </table>
      </div>
    </div>
  </div>
  

That sort of mess of logic in the view is why AngularJS got a bad rap… but! … that was never how it was supposed to be!

Here is how that HTML page looks with the content broken out into directives:


<div class="col-md-12"> 
  <div class="first-two-columns" ng-controller="RatingListController as list ">
    <div class="col-md-3 master-list">
      <tn-sortable-table 
        list-data='list' cols-displayed="name,score" cols-labels="Character,Rating" check-action='addOrRemoveFromRatedItems' sort-col='score' sort-desc='true'>
      </tn-sortable-table>
    </div>
    <div class="col-md-4 selected-list">
      <user-selected-list></user-selected-list>
    </div>
  </div> <!-- end RatingListCotnroller -->
  <div class="col-md-5 photo-display-list " ng-controller="PhotoDisplayController as photos">
    <div class="photo-box">
      <img ng-src="{{photos.chosenPhoto.photo_url}}" alt="{{photos.chosenPhoto.title}}">
    </div>
    <tn-sortable-table 
      list-data='photos' cols-displayed="title,category,year" cols-labels="Photos of Tom,Activity,Year" check-action="pickPhoto" sort-col='title' sort-desc='false'>
    </tn-sortable-table>
  </div> <!-- end PhotoDisplayController -->
</div>

 

See? The main page segments are called by custom tag with their specific code factored out.

The key element is the creation of the DSL like <sortable-table> tags.

You can use data from mutiple controllers, name the data columns you want to see, give custom column labels, and specify which columns and direction to sort by.

AND, you can dictate what action will be performed after checking…all from one directive line.

I will indent and comment for easier reading:

<tn-sortable-table 
  list-data='list' <!--we actually pass all the functions down to the directive to enable action-->
                   <!--for more than a demo controllers another param would be added here for custom data-->
  cols-displayed="name,score"<!--These Two columns of the data table will be displayed in this order-->
  cols-labels="Character Name,Rating"<!--Label the headers in a user friendly way-->
  sort-col='score' sort-desc='true'<!--On load, show table sorted by it's 'score' column descending-->
  check-action='addOrRemoveFromRatedItems'><!--this is the Tricky Part : pass a function name-->
</tn-sortable-table>

The first above lets a visitor choose names from a master list and put them into their own order.

You can see my project where you can use something similar to make Top Ten Lists and have your votes added to the public score as well as make image of your own opinions to share via social media.

And my second use of the directive, this time as a control feature for a photo display as you saw in the linked demo.

<tn-sortable-table 
  list-data='photos' <!-- same directive, different controller -->
  cols-displayed="title,category,year"<!--Three columns this time - any number of cols works-->
  cols-labels="Photos of Tom,Activity,Year"<!--user can click on any table header label to resort-->
  sort-col='title' sort-desc='false'
  check-action="pickPhoto" > 
</tn-sortable-table>

The parameters: list-data , passes all the list data downward as well as the scope needed to execute your action.

The ‘cols-displayed’ and ‘cols-labels’ do what you think.. the first is used as a key and the second as text in the table headers. Note, AngularJS will not take arrays. I chose comma separated strings as the easiest alternative to type.

It is easy to allow a content creator to choose what column the tables should be ranked on when landing with sort-col (name of the data key column) and sort-desc (true or false).

The checkAction is a string name of the function on the controller that will be called.

You can see the directive’s JS file below:

Here is the directive’s JS file that handles those the <tn-sortable-table> tags above.

// js/directives/sortable-table.js

angular
  .module('demo-directive-app')
  .directive('tnSortableTable', sortableTable);

  function sortableTable(){
    return {
      restrict: 'E',
      scope:{
        listData : "=listData"
      },
      templateUrl: 'templates/directives/sortable-table.html',
      link: function(scope, element, attrs){
        scope.colsDisplayed = attrs.colsDisplayed.split(',');
        scope.colsLabels = attrs.colsLabels.split(',');
        scope.sortCol = attrs.sortCol;
        scope.sortDesc = (attrs.sortDesc == "true");
        scope.itemsInSet = scope.listData.itemsInSet;
        scope.checkAction = function(item){
          scope.listData[attrs.checkAction](item);
        }
      }  
    };
  };

There are a few tricky things there. The kebab - separated names get ‘normalized’ into camel case in the JS file. (i.e. cols-displayed must be changed to colsDisplayed).

I use the setter format “=listData” to expand the page scope into the directive scope. An alternative way that you need when you’ll manipulate the data is the use of the ‘link: function() ‘ syntax.

I am going to cop-out on a true explanation of how this all works. That would be longer than this post all together.

Here are a few links Stack overflow essay on AngularJS inheritance and the main AngularJS developer guide on directives

I worry somewhat about the elegance of passing the entire scope via ‘listData’ however it’s important to realize …. and you might pound your head against the table for a few hours … that you can’t have ng-click’s given action build a regex or function via interpolated elements of expressions!

from their site:

No function declarations or RegExp creation with literal notation

You can’t declare functions or create regular expressions from within AngularJS expressions. This is to avoid complex model transformation logic inside templates. Such logic is better placed in a controller or in a dedicated filter where it can be tested properly.

So, that explains why I was sure that the parent scope of the controller was available to the directive via the “listData” param.

You CAN use a directive parameter to -be executed- . Ng-click looks at what is between the “quotes” and first tries to execute it rather than interpolating {{}} first then executing. Many work-arounds attempts will stop the entire page from executing.

What I do below is one working approach:

<!-- templates/sortable-table.html -->

<table id="all-performer-table" class="table table-bordered table-striped">
  <thead ng-init="sortType=colTwo; sortReverse=true ">
    <tr>
      <td sortable-table-header inner-type='sortType' inner-reverse ='sortReverse' this-col='colOne'></td>
      <td sortable-table-header inner-type='sortType' inner-reverse ='sortReverse' this-col='colTwo'></td >
      <td class="check-box-label">  
       +<span style="color: red;"> -</span>
     </td>
   </tr>
  </thead>  
  <tr ng-repeat="item in listData.itemsInSet | orderBy:sortType:sortReverse" > 
    <td ng-class="{limeHighlight: item.selected}" >
      {{item[colOne]}}
    </td>
    <td>
      {{item[colTwo]}}
    </td>
    <td>
      <input type="checkbox" ng-model=item.selected ng-click="listData[checkAction](item)">
    </td>
  </tr>
</table>
 

You’ll see that I have yet another directive, the directive, nested withing the directives.

That inner hidden directive is passed a column name to label the column from the parent parameter.

  • the single quotes around ‘colOne’ passes teh value of ‘colOne’, not the string “colOne”.
  • to retrieve the column information using that dynamic parameter, use bracket notation.

**By breaking out the header elements further, we cleans out the repeated glyphicon references. Even more importantly it is a step towards being able to iterate over an array of column names to accommodate a variable number of columns using a single directive. **

A developer won’t need to know the internal hand-off as it is encapsulated within the outer tags like a DSL.

For good measure though, here is the internal working:

// js/sortable-2col-header.js

angular
  .module('demo-directive-app')
  .directive('sortableTableHeader', sortableTableHeader);

  function sortableTableHeader(){

    return {
      scope: {
        thisCol : "=thisCol",
        innerReverse : "=innerReverse",
        innerType : "=innerType"
      },
      templateUrl: 'templates/sortable-table-header-cell.html'
    };
  };

<!-- templates/sortable-table-header-cell.html -->


<table id="all-performer-table" class="table table-bordered table-striped">
  <thead >
    <tr>
      <td ng-repeat="label in colsLabels">
        <a href=""  ng-click="
          $parent.sortCol == $parent.colsDisplayed[$index]
          ? $parent.sortDesc = !$parent.sortDesc
          : $parent.sortCol = $parent.colsDisplayed[$index]"
        >
          {{label}}
          <span ng-show="$parent.sortCol == $parent.colsDisplayed[$index] && $parent.sortDesc" class="fa fa-caret-down"></span>
          <span ng-show="$parent.sortCol == $parent.colsDisplayed[$index] && !$parent.sortDesc" class="fa fa-caret-up"></span>
        </a>
      </td >
      <td class="check-box-label">  
       + 
       <span style="color: red;"> -</span>
     </td>
   </tr>
  </thead>  
  <tr ng-repeat="item in itemsInSet | orderBy:sortCol:sortDesc" > 
    <td ng-repeat="col in colsDisplayed" ng-class="{limeHighlight: item.selected}" >
      {{$parent.item[col]}}
    </td>
    <td>
      <input type="checkbox" ng-model=item.selected ng-click="$parent.checkAction(item)" >
    </td>
  </tr>
</table>
 

I hope these examples might help someone toying with the same issues out.

Note that last line!

      <input type="checkbox" ng-model=item.selected ng-click="$parent.checkAction(item)" >

That might have been worth reading this blog entry alone if you’d been in a pinch. (LOL).

I could point out more in the template above, but most of it is basic sorting and filtering mechanisms that make AngularJS so easy to use for state centric displays.

I will -not- share the two controllers code with you. They’re very basic scaffolds to let the demo actually do something when you click.

Again, check out the live demo And/Or you can check out the entire little sandbox in this angularjs-custom-directive-demo GitHub repository.

Within that Repo you can check out a few earlier iterations, AND check out my more extensive and ambitious handlebars solution to the same types of displays but with more robust and reusable controllers and interesting approaches to making the pages maintain display state without watches and two way binding.

Post on that will be linked here soon!.




If you’re like me you often learn new technologies by doing while you read the manuals.

In re-exploring the preferred syntax for AngularJS directives, I was coding along with their developer guide . I found that my Chrome browser was throwing cross origin errors when using simple examples like:


.directive('myCustomer', function() {
  return {
    restrict: 'E',
    templateUrl: 'templates/sortable-table.html'
  };
});

The dev-tools console error:


XMLHttpRequest cannot load file:///Users/tomnorian/Documents/tutorials/demo-code/templates/sortable-table.html. Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https, chrome-extension-resource.

I hadn’t had this problem in the past as I was usually running a NodeJS or Rails server…but when you’re learning code in the sandbox it might just be you and your sleek code editor.

I came across this StackOverflow thread which is still a good resource for this CORS issue.

The problem seems to be more isolated to Google Chrome, although at it’s essence they are putting in a good security constraint on the browser. The best work-arounds from the thread were to serve your static development project, or use an IDE that does, rather than relying on simply opening the HTML pages directly as you usually can.

A really nice open source tool is created and maintained here: http-server:a command-line HTTP server

If you have node installed on your development machine all you need to do is:


 npm install http-server -g

to use it their directions are:

 http-server [path] [options]

More explicitly:

  • you can simply navigate to the folder of your project you would like to serve and then type ‘http-server’ in your bash terminal to have that directory you are in served as a root.

What you’ll see:

 demo-code :> http-server
Starting up http-server, serving ./
Available on:
  http://127.0.0.1:8080
  http://10.0.0.5:8080
Hit CTRL-C to stop the server
[Wed Mar 01 2017 11:30:13 GMT-0800 (PST)] "GET /index.html" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"


Open your browser to http://127.0.0.1 and if you have a index.html file it will be served to you; or type in the filenames (with additional relative paths, if nested) to see them.

That quickly got me around the Chrome CORS issue and onto working AngularJS directive templates; working directly from Sublime Text 2 and Chrome.

This post is mostly to give those guys working on the http-server some well deserved love!