Wednesday, June 22, 2016

NPM & Webpack - the barest of bones!

I'm still a developer in training (but, then again, aren't we all?) and NPM has been a particular challenge in my learning process. Important disclaimer: I don't pretend to have a full grasp of NPM, yet. But writing about it helps me to sort it out and, hopefully, further my understanding.

NPM is basically a build tool. What the heck is a build tool? Yeah, I'm not great at explaining that yet but generally I 'get' that it takes your code and compiles it into a folder that is optimized for production. I understand that it plays well with dependencies between multiple files, but having an example of that is a bit further down my path.

Edit: Big error above. NPM is a node package manager, that can be USED as a build tool because of scripts. It is not just a build tool, but rather, that is one of the uses of NPM. Webpack, a module bundler, is more the 'builder'.  See! I am still trying to make sense out of all of this!

For now, I know how to use Webpack to take my html, css, javascript and assets (pictures, etc.) and put them into a 'build' file which is ready for production. Additionally, I no longer have to put any <link> or <script> tags in my html. Webpack does it for me!

I understand that these tools are designed to streamline your process and eliminate tasks that you are used to doing over and over again. I've discovered, at this point, two tasks that are now automated and are making my life easier: building my file tree and publishing my code to gh-pages on GitHub.

I won't particularly explain what every line in my code does, but rather present it with confidence that it works.

Additionally, I won't explain how to install and set up NPM on your machine the first time. Helpful directions can be found here:

Here goes nothing!

Starting a Project with NPM

This is MY process every time I start a new project. We all do it differently. I won't say mine is the best, but it serves me well.

1)  In the terminal I navigate to the folder where I plan on putting my project folder. There, I build my project folder:

          md npmworkflow

2)  Navigate into the new folder:

          cd npmworkflow

3)  I always (always!) start my git flow, initialize my project and create my repository on GitHub:

       git init
     git remote add origin https://github.com/myusername/npmworkflow.git

4)  Time to start working with NPM. The first step is to initialize NPM:

          npm init

5)  A number of questions will be presented. You do not have to answer them, but I answer a few:

          name:
          version:
          description: a NPM workflow basic project
          entry point: js/index.js
          test command:
          git repository: (self populated when git init is 1st)
          keywords:
          author: Ryan S. Buchholtz
          license:

6)  Lastly, you will be asked "Is this ok?". Simply press enter.

7)  Back to the root of your project folder. Want to see what has happened with the npm int?

          ls

8)  A new file has been added to your project. The package.json file was the result of the npm init. Time to head to Sublime and have a look around!

9)  Open the package.json file. It should look a little something like this:


{
  "name": "npmworkflow",
  "version": "1.0.0",
  "description": "a NPM workflow basic project",
  "main": "js/index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/myusername/npmworkflow.git"
  },
  "author": "Ryan S. Buchholtz",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/myusername/npmworkflow/issues"
  },
  "homepage": "https://github.com/myusername/npmworkflow#readme"
}
10)  Time to make some changes. I overwrite the scripts with the following. Study them and try to make sense out of what each one does. Many make perfect sense - they are just terminal commands that have been wrapped in a script so that they can be called using npm run:

"initialbuild": "npm run build:assets && npm run build:initialhtml && npm run build:initialcss && npm run build:initialjs && npm run build:initialwebpack", 
"build:assets": "mkdir -p assets", 
"build:initialhtml": "touch index.html", 
"build:initialcss": "mkdir -p css && touch css/style.scss && touch css/normalize.scss, 
"build:initialjs": "mkdir -p js && touch js/index.js", 
"build:initialwebpack": "touch webpack.config.js", 
"mkdir": "mkdir -p build", 
"build": "npm run clean && npm run mkdir && webpack", 
"watch": "npm run build && webpack --watch", 
"clean": "rm -rf build", 
"deploy": "gh-pages -d build"
11)  File save and navigate back to the terminal. Make sure you are in the project folder and then enter the following script (seen above):

          npm run initialbuild 

12)  What just happened? Well, for me, a nice short-cut. My file tree and files have been built! Go back to Sublime and take a look. You should see 3 new folders (assets, css, js) and 2 new files (index.html, webpack.config.js). COOL!! But it's time to install a bunch of dependencies for your project. In my case, these are the bare bones.

13)  Using the terminal again, run the following commands to install some NPM packages for development (not necessary in production of your product):

     npm install --save-dev webpack     
     npm install --save-dev copy-webpack-plugin 
     npm install --save-dev css-loader
     npm install --save-dev extract-text-webpack-plugin 
     npm install --save-dev gh-pages 
     npm install --save-dev html-webpack-plugin 
     npm install --save-dev style-loader
     

Shortcut: you can chain these together into one command e.g. npm install --save-dev css-loader webpack style-loader, etc.

14)  There is at least one NPM package that I install that I would need in production, and that is jQuery. Go ahead and install if necessary:

     npm install --save jquery

15)  Navigate back to Sublime and your package.json file. You will see a list of dependencies that have been installed - in two lists - those for development (devDependencies) and those for production (dependencies). Voila!

16)  With all these installed they are kinda just sitting there, but are not invoked to 'get to work'! That all happens in the webpack.config.js file. Go ahead and open that up and add the following to that file:

var path = require('path');
var packageData = require('./package.json');
var filename = [packageData.name, packageData.version, 'js'];

var HtmlWebpackPlugin = require('html-webpack-plugin');
var webpack = require('webpack');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var CopyWebpackPlugin = require('copy-webpack-plugin');



var plugins = [
    new HtmlWebpackPlugin({
      inject: 'head',
      template: 'index.html',
      minify: {
        "collapseWhitespace": true, 
        "removeComments": true, 
        "removeRedundantAttributes": true, 
        "removeScriptTypeAttributes": true, 
        "removeStyleLinkTypeAttributes": true
      }
    }),
    new ExtractTextPlugin('style.css'),
    new CopyWebpackPlugin([
          {from: 'assets', to: 'assets'}
      ])
  ];

module.exports = {
    entry: {
      main: [
        path.resolve(__dirname, packageData.main)
      ]
    },

    output: {
        path: path.resolve(__dirname, 'build'),
        filename: filename.join('.'),
    },
    devtool: 'source-map',
    plugins: plugins,
    module: {
      loaders: [
        { 
            test: /\.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader")
          }
      ]
    }

};

17)  Save that file! Study it (could take hours, it did for me!) and try to make sense out of each line and how it interacts with your project. Each line comes from the ReadMe page for each of the dependencies that were installed and listed in the package.json file. They have been linked in steps 13 and 14. 

18)  Before going any further, let's populate a few files with some code so that we can actually see what happens when we run webpack:

index.html


<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <title>Document</title></head><body><div class="first-div">  <h1>My Name is Ryan</h1></div><div>  <p>I'm a Monkey!</p></div>
</body></html>


style.css

@import url(normalize.css);
* {  background-color: orange;}
body {  width: 60%;  margin: 0 auto;  border: 1px solid green;  height: 1000px;  background-color: blue;}
.first-div {  background-color: green;  width: 50%;  margin: 0 auto;  border: 5px solid red;}
.first-div h1 {  font-size: 5em;  text-align: right;  background-color: white;}

normalize.css

copy and paste from https://necolas.github.io/normalize.css/4.1.1/normalize.css

index.js
var $ = require('jquery'); 
require('../css/style.css'); 
$(document).ready(function(){  alert("I'm from index.js");});


19)  Make sure all those files are saved and let's run our first build script. In the terminal:

     npm run build

20)  Back to Sublime and an exploration of our file tree.  See anything new? Yup! A folder called build. Dive into there and have a look around. You will see all the files necessary to launch this little project. Open them one at a time, starting with index.html:

This file now has two things that the original index.html didnt - a <script> and a <link>, referencing your javascript and css files.  Cool, eh? View this file in the browser and the style from the css and the alert from the js work. I find that awesome.

One thing you may notice is that the asset folder did not copy over into the build folder. Why? Near as I can tell this is because there is nothing in the folder. If you add a single file and re-run the npm run build command you will see it copy over.

In that last paragraph you will see that I mentioned a change in code, and then told you to re-run npm run build. That suggests that, every time you make the smallest change to your html, or css, or javascript - well, anywhere - you would need to re-run npm run build. That doesn't really jive with the idea that this tool eliminates repetitive actions, does it? Well, there is a solution:

     npm run watch

What did you notice when you ran that command in the terminal? Did it throw up some messages, and then never return you to the prompt that you are used to? Hopefully it did, because that now means that a 'watch' event is happening. To test it, go into your css file and make a change. I changed the background color of * to lightorange and saved the file.

You can now do one of two things - if index.html is still open in your browser, refresh it. Did the background color change? Yup, it did! You can validate this by going to the build folder and opening the style.css. Scroll to the bottom and you will see that the code in the build folder has been changed to reflect the changes you made in the original style.css. LOVE THAT!

The same would be true for all of your files that are used to create the build folder.

Also - because this watch command kind of locked up your terminal, you will need to open another terminal window to continue to use it. When you are done with the watch, click ctrl-c and it will cancel the watch.

One more thing that I find useful for this stage in development that I want to share. Head back to the terminal and install this package:

     npm install webpack-dev-server -g

Something a little different here - the -g. This has been installed globally and is not a dependency of this project. You can use this any time going forward, wherever you might be and not need to re-install it.

With the install complete, go ahead and run it in the terminal (make sure you are in the parent folder of your project).

     webpack-dev-server

The first line it returned, upon being run, was an http:// address. Copy this and open it in your browser. Change something in your css - I changed the .first-div background color to yellow. Save the file and navigate back to your browser. The change has been reflected and you didn't have to refresh or re-run build! WOWZERS! I love this.

Alrighty. This is what I feel comfortable writing about for now. I have been able to incorporate some different packages like font-awesome and css-animations and will share when I feel more confident. What I do know for now: this NPM/Webpack code works. But it doesn't really do THAT much. It doesn't minify my code, it doesn't make dependencies, and it doesn't do THAT much more than I'm capable of doing on my own. But I feel that this foundation is necessary to continue to dive deeper and figure out all the ways that NPM will help to improve my skill set and make me that much better of a programmer.

Happy coding! Look forward to hearing from any and all!

Friday, March 25, 2016

Thinkful....and Street Fighter!

TL;DR - I built a Street Fighter page that does a few fun things.  The finished product is here:

http://ryanscottbuchholtz.github.io/jquery-streetfighter/

Last week I began my enrollment with Thinkful, an on-line 'bootcamp-esq' coding academy.  It's another step forward in my mission to become a front-end developer and I couldn't be more excited.

A number of the initial projects were fairly rudimentary after all the work I've done with TeamTreehouse, but they were fun nonetheless.  Last night was a pretty cool jQuery exercise - cool enough that I thought I should share it with you.

The objective:  given a number of .gif & .png assets, write the code to a) display different images when the mouse hovers over them, b) create actions that happen with the click of the mouse and c) generate a specific action when pressing a keyboard key.  Not rocket science, but a challenge for a newbie and deeply satisfying when accomplished!

Street Fighter was a video game from the late 80's and continues to be one of the most popular video games of all time.



In the game, the main player controlled a character named Ryu.  He was a martial artist and had a number of specific attacks, executed with button/pad combinations.  One of them, Hadouken, is a key accomplishment of this jQuery exercise.  Write the code that moves Ryu into the attack mode and makes him throw a Hadouken.

To tackle this project I wrote pseudo-code that looked like this:


  1. Create a div that holds the image assets, while as default, displays ryu in 'standing still' position.
  2. Create a second div that positions the Hadouken in the initial place where it would begin to be thrown from Ryu's hands, but do not display unless a specific activity is initiated.  
Easy enough!  Taking advantage of the CSS feature display:none I was able to basically 'layer' 4 different images of Ryu while displaying only one.  The HTML looks like this:


<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Streetfighter</title>
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/main.css">
</head>
<body>
  <div class="main">
    <div class="ryu">
      <div class="ryu-still ryu2"></div>
      <div class="ryu-ready ryu2"></div>
      <div class="ryu-throwing ryu2"></div>
      <div class="ryu-cool ryu2"></div>
    </div>
   </div>
</body>
</html>

Inside of my main div I've created another div called ryu.  Further down, inside of that div are 4 separate divs, each designed to hold a specific Ryu image.  The CSS for those 4 divs looks like this:

.ryu,
.ryu-ready,
.ryu-still,
.ryu-throwing,
.ryu-cool {
  width: 659px;
  height: 494px;
}

.ryu-ready,
.ryu-throwing,
.ryu-cool {
  display: none;
}

.ryu2 {
  background-repeat: no-repeat;
}

.ryu-ready {
  background: url('../images/ryu-ready-position.gif');
}

.ryu-still {
  background: url('../images/ryu-standing-still.png');
}

.ryu-throwing {
  background: url('../images/ryu-throwing-hadouken.png');
}

.ryu-cool {
  background: url('../images/ryu-cool.gif');
}

Width/height and background has been defined for each div.  Additionally, you see that three of the divs have a display:none, which means, currently, only one div will display when the page is visited - .ryu-still.




Now it's time to get this Hadouken appropriately positioned.  First, I created another div just for the Hadouken, immediately under the <div class="ryu">.  It looks like this:  

<div class="hadouken"></div>

Nothing special, just a 'box' to store the hadouken.gif.  The challenge, however, is the position the Hadouken so that it is right by Ryu's outstretched hands when he is ready to throw it.  His position would look like this:



This can be accomplished using position: absolute and assigning top: and left: values.  Remember, using position: absolute only works when the parent element has a position: relative.

Using Chrome Developer Tools you can easily change the values in top: and left: until your get the Hadouken to where it belongs.  The initial position of the Hadouken looks like this:




With Chrom Dev Tools, seen below, you can start to experiment with the top: and left: position of the div until it is where you need it to be.




When all is said and done, the css for the Hadouken looks like this:

.hadouken {
  background: url('../images/hadouken.gif');
  width: 156px;
  height: 90px;
  display: none;
  float: left;
  position: absolute;
  top: 168px;
  left: 617px;
  z-index: 10;

}

A z-index of 10 is used to make sure the Hadouken remains above other elements on the page. And, remember, the default display of the Hadouken is hidden.  We only want to see it when the user does something.

Easy breezy!  I haven't stepped out of HTML and CSS, so this is all, at this point, second nature.  But now, it's time to dabble in jQuery and try to create some interactivity for this bad boy!  More pseudo-code:


  1. When mouse hovers over Ryu, hide still Ryu and show ready Ryu
  2. When mouse no longer hovers over Ryu, hide ready Ryu and show still Ryu
After a little research focused on 'hover', I discovered a jQuery method called .mouseenter.  The method binds an event handler to be fired when the mouse enters an element.  In this case, the element we want to trigger the event handler is <div class="ryu"></div>, which is the parent element to the 4 different Ryu images.  Sidebar:  I think hover can be used, but mouseenter, being a little more robust, made more sense to me at this point.


Into my app.js file to start writing some new code!  Remember to link this sheet to your HTML, otherwise, all the code you write will do nada, zippo!

As with all jQuery, it is advisable to wrap your code inside of:

$(document).ready(function() {

});

I know there are different philosophies, specifically re: where you put your <script> tags in your HTML.  If the <script> is put just before the </body> tag it is assumed that your document will be 'ready', thus negating the need for the code above.  I use it anyways, just in case.  It's 2 lines.  Doesn't hurt.

Converting my pseudo-code into real code yielded the following:

  $('.ryu').mouseenter(function(){
    $('.ryu-still').hide();
    $('.ryu-ready').show();
  
  }).mouseleave(function(){
    $('.ryu-still').show();
    $('.ryu-ready').hide();

  });

It couldn't be closer to a literal translation of my pseudo-code.  .mouseenter and one element hidden, one shown.  .mouseleave, the opposite.  Perfecto!

On to more pseudo-code to tackle the next goal of Ryu, which would be throwing the Hadouken when he is clicked on with the mouse.


  1. On mouse click, hide all images of Ryu, but show the image of Ryu throwing.
  2. On mouse click, show the Hadouken and have it move from left to right.
  3. When mouse is unclicked, hide all image of Ryu, but show the image or Ryu still.
Digging into jQuery, I found the methods called .mousedown and .mouseup.  These seemed perfect to accomplish the goals.  Similar to .mouseenter and .mouseleave, they bind an event handler to the event.

My code, using these methods, initially looked like this:


  $('.ryu').mousedown(function(){
    playHadouken();
    $('.ryu-still').hide();
    $('.ryu-ready').hide();
    $('.ryu-throwing').show();
    $('.hadouken').show();
  });

  $('.ryu').mouseup(function(){
    $('.ryu-still').show();
    $('.ryu-ready').hide();
    $('.ryu-throwing').hide();
    $('.hadouken').hide();
  });

Upon running in my browser, a few things work - specifically around which image of Ryu is displayed and then hidden.  But Hadouken caused some problems.  It was displayed and hidden, but it didn't move.  On to do some more digging in the jQuery API documentation.

Clicking on Effects, the first technique listed is .animate().  Seems like a good place to start.

The .animate() method has this basic syntax:

.animate( properties [, duration ] [, easing ] [, complete ] )

Digging a little deeper:


  • Properties:  An object of CSS properties and values that the animation will move toward.
  • Duration: A string or number determining how long the animation will run.
  • Easing: A string indicating which easing function to use for the transition.
  • Complete: A function to call once the animation is complete, called once per matched element.
So back to handy-dandy pseudo code I go, in an attempt to make sense of this method and use it for the first time.


  1. Move the Hadouken image to the right, which means adjusting the left: value in the css and setting it equal to a place where the animation should end.
  2. Get the Hadouken to its ending position in some amount of time.  500 = 500 milliseconds.  The higher the number, the slower the animation.
  3. When the Hadouken image has reached the end it should be hidden and the left: value should be reset to the initial value specified in the css.
OK.  That all seems to make sense.  Time to convert it to jQuery code, and it looked something like this (all inside of the .mousedown method):


 $('.hadouken'.finish().show().animate(
      {'left': '1020px'},   (end goal of element)
      500,                  (length of animation)
      function() {          (complete function)  
        $(this).hide();
        $(this).css('left', '617px');
      }
    );
  });

Because of the function() run inside of .animate, we no longer need to .hide() the Hadouken element on the .mouseup method.  It happens as soon as the animation is complete, per the function written inside of the .animate() method.  Wow!  Awesome.  

One outstanding issue is the completion of the .animate when the user clicks repeatedly.  In doing so, the animation will continue, regardless of how many times the user clicks on Ryu.  To fix that, the .finish() method is called before .show().  .finish() will stop the currently running animation and reset CSS to the settings in the last function inside of .animate().  Voila!

With that code I now have a cool Martial Artist throwing some mean Hadouken.  Very, very cool.





But there is more!  There is still one image of Ryu that has remained hidden.  Ryu looking damn cool. 




The goal is to only display this image when a certain key (the letter x) is pressed on a keyboard.  Hi ho, hi ho it's off to jQuery API I go!

Knowing what I know, it's safe to presume there must be some kind of .keydown() and .keyup() method in jQuery and, what would you know, there sure is!  And they work an awful lot like .mouseenter(), .mouseleave(), .mousedown() and .mouseup().

Because we only want the specific activity to occur when the 'X' key is pressed, it appears that you can pass an event object (e) to the handler function and examine it.  That code looks a little something like this:

  $(document).keydown(function(e){
    if(e.keyCode === 88) {
      $('.ryu-still').hide();
      $('.ryu-throwing').hide();
      $('.ryu-cool').show();
    }
  });
  $(document).keyup(function(e){
    if(e.keyCode === 88) {
      $('.ryu-still').show();
      $('.ryu-throwing').hide();
      $('.ryu-cool').hide();
    }

  });

e, the event object is passed to the handler function() and is then examined.  Because every button on your keyboard has a numeric value, I had to look that up and found that the letter x is equal to 88.  So, the if statement must be true for the remains jQuery code to be executed.


There is one last feature that would be awesome - playing the trademark sound when the Hadouken is thrown.  Some code needs to be structured that is executed on the .mousedown() method.

There is a .mp3 stored in a sound folder, of that specific sound.  HTML5 has an <audio> tag and it makes sense to plug that in so we can use the file when we need it.  To do so, inside of the HTML file, and just above the <script> tags, I added in the following:

<audio id="hadouken-sound" src="sound/hadouken.mp3"></audio>

It's been given an id so that we can reference the file using jQuery.

Because, when working with an audio file there are a number of methods that must be called, it makes sense to wrap all of those into a single function, and then call that function when it makes sense - specifically, when Ryu is clicked on.  That function looks like this:

function playHadouken() {
  $('#hadouken-sound')[0].volume = 0.5;
  $('#hadouken-sound')[0].load();
  $('#hadouken-sound')[0].play();

}


Because we are working with an embedded audio file, we are not using jQuery methods, but rather JavaScript being called on DOM elements.  By using $('#hadouken-sound')[0] we are using a jQuery syntax, but it could be typed like this, too:

document.getElementById<'hadouken-sound').volume=0.5

The [0] specifies the first element with the id #hadouken-sound, and is unnecessary in the JavaScript syntax because there should only ever be one element per ID name.

I've named the function playHadouken and, inside of the function, three things happen, all of which are using .  First, the volume level is set (range is 0 to 1), then the .load() method is called, which is simply a method which fetches data from the server.  Lastly, .play() is called, which plays the audio file. 

The function is now placed inside of the .mousedown method, and the very beginning of the function.  The finalized function now looks like this:

  $('.ryu').mousedown(function(){
    playHadouken();
    $('.ryu-still').hide();
    $('.ryu-ready').hide();
    $('.ryu-throwing').show();
    $('.hadouken').finish().show().animate(
      {'left': '1020px'},
      1000,
      function() {
        $(this).hide();
        $(this).css('left', '617px');
      }
    );

  });


And that's it!  I know there are some quirks, and I will report back when I a) find them and b) fix 'em!

Happy Coding!

Thursday, March 3, 2016

The Flexbox Fun!

Had a lot of fun working with the Flexbox model today.  Wanted to share with you my 'crib sheet' that I will refer to until it is firmly embedded in my own memory.  Enjoy!


Tuesday, February 16, 2016

A simple To Do list in JavaScript isn't so simple for a newbie!

I really like Team Treehouse.  It has taught me so much and usually in a manner that really resonates with me.

And sometimes, no matter how many times I do a lesson, I just don't get it.  I have to sleep on it.  I have to do side research.  And I have to break it down into micro-steps.

This is a case where, after going through all my own methods towards understanding (and, candidly, a nearly tearful moment) the lightbulb illuminated and it became my mission to share my understanding in the event that anyone else was as stuck as I was.

My notes, both digital and paper form, for one (in retrospect) little challenge
So let's start at the beginning.  As a JavaScript programmer you have received a 'mock-up' of a basic To Do list.  The HTML and CSS are written but there is no functionality.  Here is what you have:

From here we know a few things:


  1. User should be able to add a new item
  2. Once item is added, user should be able to edit the item, or delete the item.  This should be able to be done if the item resides in the TODO list or the COMPLETED list.
  3. User should be able to place a check in the checkbox to move an item from the TODO list to the COMPLETED list.  Conversely, if the checkbox is unchecked, the item should move from the COMPLETED list back to the TODO list.
Easy enough, right?  Well, it's a lot of code to accomplish this and everyone approaches it differently.  Here is how I approached it:

First things first.  Let's take a look at the HTML code for this To Do list.  It's pretty simple:


<!DOCTYPE html>
<html>
  <head>
    <title>Todo App</title>
    <link href='http://fonts.googleapis.com/css?family=Lato:300,400,700' rel='stylesheet' type='text/css'>
    <link rel="stylesheet" href="css/style.css" type="text/css" media="screen" charset="utf-8">    
  </head>
  <body>
    <div class="container">
      <p>
        <label for="new-task">Add Item</label><input id="new-task" type="text"><button>Add</button>
      </p>
      
      <h3>Todo</h3>
      <ul id="incomplete-tasks">
        <li><input type="checkbox"><label>Pay Bills</label><input type="text"><button class="edit">Edit</button><button class="delete">Delete</button></li>
        <li class="editMode"><input type="checkbox"><label>Go Shopping</label><input type="text" value="Go Shopping"><button class="edit">Edit</button><button class="delete">Delete</button></li>
        
      </ul>
      
      <h3>Completed</h3>
      <ul id="completed-tasks">
        <li><input type="checkbox" checked><label>See the Doctor</label><input type="text"><button class="edit">Edit</button><button class="delete">Delete</button></li>
      </ul>
    </div>

    <script type="text/javascript" src="js/app.js"></script>

  </body>
</html>


It's pretty simple.  One <div>, a <p> to hold the new item input <input>, a <ul> for the incomplete list and a <ul> for the completed list, along with <button> and <input> items for all tasks once entered.

After studying this I can come up with 4 elements that we will probably need to be able to reference and/or manipulate inside of our JavaScript code.  They are:  the task input, the Add button, the list of incompleted tasks and the list of completed tasks.  That's where I start.  Let's see how to declare variables and assign their value to each of these items

The new task input


The id for this input is "new-task".  Using the JavaScript method getElementbyId we can assign this to a new variable.

               var taskInput = document.getElementById("new-task");

The addButton

The button doesn't have an id.  But it has a tag name and that is "button".  JavaScript has a method called getElementsByTagName.  If you study that method name you will notice it says Elements, not just Element.  That would suggest that it could return more than one item and it's safe to assume that, if there are more than one, the return would be in the form of an Array. After studying the HTML I know that we are looking for the first button and, in an array, grabbing the first button can be done by using the index of [0]

               var addButton = document.getElementsByTagName("button")[0];

The two lists - incomplete and complete

These two lists each have an id.  We can use the same method we used to get the "new-task" item.

     var incompleteTaskList = document.getElementById("incomplete-tasks");
     var completedTaskList = document.getElementById("completed-tasks");

In JavaScript these variables should be defined at the very top of your code.  You will see, as we go, that it is standard practice to keep your code organized for both you, and the next person who may have to work with it.

So, in summary, here is what the top of your JavaScript code should look like:

var taskInput = document.getElementById("new-task");
var addButton = document.getElementsByTagName("button")[0];
var incompleteTaskList = document.getElementById("incomplete-tasks");
var completedTaskList = document.getElementById("completed-tasks");

We now have 4 variables that we can act upon as we progress through the challenge of creating a To Do list that does what we want it to do!  We are on our way.

For me, the logical next step is creating a way to add a new task to the incomplete task list.  We can do that by writing a function that takes the information that the user has entered and builds a task that looks like the existing tasks.  Let's revisit the HTML code of an existing task:

<li><input type="checkbox"><label>Pay Bills</label><input type="text"><button class="edit">Edit</button><button class="delete">Delete</button></li>

To break it down further we have the following:

1. <li>
  2. <input>
  3. <label></label>
  4. <input>
  5. <button></button>
  6. <button></button>
</li>

There are six components to a new task and we need to build them each time a user enters a new task. We should build this element inside of a function and that function should return a complete task item element.  We call this function createNewTaskElement.  Remember, inside of this function all we do is build the task element.  It won't do anything with the element (like add it to the incomplete list) until it is asked to do so.

var createNewTaskElement = function(newTaskUserInput) {
     var listItem = document.createElement("li");
     var checkBox = document.createElement("input");
     var userInputLabel = document.createElement("label");
     var editableUserInput = document.createElement("input");
     var editButton = document.createElement("button");
     var deleteButton = document.createElement("button");
}

We've mirrored the HTML for the Task Element and declared variables inside of the formula, each a creation of the 5 components of each To Do <li>, including the <li> itself.  As variables we are now able to act upon them, modify them and append them to create a complete <li>.

Next, let's modify the two <input> variables so they reflect their type:

     checkBox.type = "checkbox";
     editableUserInput.type = "text";

We have buttons that need both labels and classes assigned to them.  We can add this information:

     editButton.innerText = "Edit";
     editButton.className = "edit";
     deleteButton.innerText = "Delete";
     deleteButton.className = "delete";

And now we need to make the value of the userInput equal to the argument that is passed to this function, the newTaskUserInput:

     userInputLabel.innerText = newTaskUserInput;

Now that everything has its values, classes and labels we can chain them together to create a single object.  We start with the listItem and add to it:

     listItem.appendChild(checkBox);
     listItem.appendChild(userInputLabel);
     listItem.appendChild(editableUserInput);
     listItem.appendChild(editButton);
     listItem.appendChild(deleteButton);

And, finally, the last thing the function should do is return the completed object:

     return listItem;

Our JavaScript code should now look like this:

var taskInput = document.getElementById("new-task");
var addButton = document.getElementsByTagName("button")[0];
var incompleteTaskList = document.getElementById("incomplete-tasks");
var completedTaskList = document.getElementById("completed-tasks");

var createNewTaskElement = function(newTaskUserInput) {
     var listItem = document.createElement("li");
     var checkBox = document.createElement("input");
     var userInputLabel = document.createElement("label");
     var editableUserInput = document.createElement("input");
     var editButton = document.createElement("button");
     var deleteButton = document.createElement("button");

     checkBox.type = "checkbox";
     userInputLabel.type = "text";

     editButton.innerText = "Edit";
     editButton.className = "edit";
     deleteButton.innerText = "Delete";
     deleteButton.className = "delete";

     userInputLabel.innerText = newTaskUserInput;

     listItem.appendChild(checkBox);
     listItem.appendChild(userInputLabel);
     listItem.appendChild(editableUserInput);
     listItem.appendChild(editButton);
     listItem.appendChild(deleteButton);

     return listItem;

}

It's a good time to take a break (and a breath!) and revel in what has been accomplished.  A function has been written that builds a complete task item with all the html around it.  Exciting!

Onward!

What's next?  Well, we have a function but we need to devise a way to call it when the user presses the add button.  Let's do that inside of a new function.  We will call it addTask.

     var addTask = function (){
}

Inside of this function the first thing we should do is create a new variable who's value is equal to the user input.  We do this by calling the function defined above and passing the value of the taskInput variable.  (Remember, this variable, declared at the top of our JS document, holds the HTML object with the id="new-task" - <label id="new-task> - which is where the user input is placed.)  We use the .value JS method to just get the value and not the surrounding HTML.

     var listItem = createNewTaskElement(taskInput.value);

Great!  We now have a variable that is a complete task element with the value equal to what the user entered.

Logically it now makes sense that we need to move that object to the end of the incompleted tasks list.  We have a variable holding that entire list - incompleteTaskList - and we can now add to it by using the .appendChild method.

     incompleteTasksHolder.appendChild(listItem);

Ta-da!  We have a new function:

     var addTask = function (){
     var listItem = createNewTaskElement(taskInput.value);
     incompleteTaskList.appendChild(listItem);
}

But wait.  We know that functions will only do their job when called.  Inside of the addTask function we have called the createNewTaskElement function so it has done it's job.  But how to we call the addTask function?  And when should it be called?  Oh yeah - when the Add button has been pressed!  
Using an Event Listener method on the addButton variable we can wait patiently for an event and, when it occures (clicking the button) the addTask function will be called.

     addButton.addEventListener("click", addTask);

That's it!  If you are following along and have this code you should be able to add unlimited To Do items to your list.  Have a look and then see what doesn't work or doesn't look right!  That's up next. 

Alrighty!  If you've had a chance to look, this is probably what you are seeing:

And this is a complete picture of your code as it exists now:

var taskInput = document.getElementById("new-task");
var addButton = document.getElementsByTagName("button")[0];
var incompleteTaskList = document.getElementById("incomplete-tasks");
var completedTaskList = document.getElementById("completed-tasks");

var createNewTaskElement = function(newTaskUserInput) {
     var listItem = document.createElement("li");
     var checkBox = document.createElement("input");
     var userInputLabel = document.createElement("label");
     var editableUserInput = document.createElement("input");
     var editButton = document.createElement("button");
     var deleteButton = document.createElement("button");

     checkBox.type = "checkbox";
     userInputLabel.type = "text";

     editButton.innerText = "Edit";
     editButton.className = "edit";
     deleteButton.innerText = "Delete";
     deleteButton.className = "delete";

     userInputLabel.innerText = newTaskUserInput;

     listItem.appendChild(checkBox);
     listItem.appendChild(userInputLabel);
     listItem.appendChild(editableUserInput);
     listItem.appendChild(editButton);
     listItem.appendChild(deleteButton);

     return listItem;

}

var addTask = function (){
     var listItem = createNewTaskElement(taskInput.value);
     incompleteTaskList.appendChild(listItem);
}

addButton.addEventListener("click", addTask);


I personally celebrated the fact that my new task, Buy a Monkey, showed up in the TODO list.  Exciting!  But there are a couple of things that are broken:
  1. The edit button doesn't work
  2. The delete button doesn't work
  3. The checkbox takes a check, but does nothing
  4. Our last task remains in the Add Item input box

Because I like small victories I decided to tackle number 4 first.  Shouldn't be too hard, should it?  Well, it isn't.  Simply add code to the end of the addTask function that resets the value of the input box to blank:

     taskInput.value = "";

Easy breezy:


Time to tackle those buttons.  They don't work.  And it seems like we should write a function that tells those buttons what they should do and then that function should be called every time a new list item is created using addTask.

I like to be very literal at this point in my learning.  The function we need to write needs to make the buttons work so:

var makeButtonsWork = function() {

}

We need to pass the newly created listItem to this function so that, inside of the function, we can isolate the buttons that belong to that object.

var makeButtonsWork = function(taskListItem) {

}

We have two buttons that we need to work with.  We should assign each to a variable to we can call its class (edit button has a class of "edit", delete button has a class of "delete") on it.

var makeButtonsWork = function(taskListItem) {

     var editButton = taskListItem.querySelector("button.edit");
     var deleteButton = taskListItem.querySelector("button.delete");
}

We've got two variables, one holding the edit button and one holding the delete button.  We can now tell the page to go something when each button is pressed by using .onclick.

var makeButtonsWork = function(taskListItem) {

     var editButton = taskListItem.querySelector("button.edit");
     var deleteButton = taskListItem.querySelector("button.delete");

     editButton.onclick();
     deleteButton.onclick();
}

Well, we have a placeholder inside of the (), but we haven't yet told the program what to do .onclick.  We want a function run!  Time for two new functions.  Let's start with delete:

var deleteTask = function() {

}

Voila!  We've defined a function.  Thinking about it logically, when the function is run (i.e, when the delete button is pressed) we should remove the <li> from the <ul>.  So, inside of the function we need to create a variable that captures the current list item - the one that belongs to the button that has been pressed.  We have a neat tool called this.parentNode.  This, in this case, is the button.  When we use .parentNode it will return the parent of the button.  Let's look at the HTML again:


<l
i><inputtype="checkbox"><label>Pay Bills</label><input type="text"><button class="edit">Edit</button><button class="delete">Delete</button></li>

What we know is that the <button> is a child of the <li>.  So to capture the <li> we just declare a variable equal to this(this button).parentNode(the li):


var deleteTask = function() {
     var listItem = this.parentNode;
}

We can then traverse further up the DOM by calling the .parentNode on the listItem, returning the <UL>.

var deleteTask = function() {
     var listItem = this.parentNode;
     ul = listItem.parentNode
}

We now have the <ul> and the specific <li> identified and assigned to a variable.  We can easily remove the <li> from the <ul> using .removeChild().

var deleteTask = function() {
     var listItem = this.parentNode;
     ul = listItem.parentNode

     ul.removeChild(listItem);
}

Add that back into the makeButtonsWork function and you get this:

var makeButtonsWork = function(taskListItem) {

     var editButton = taskListItem.querySelector("button.edit");
     var deleteButton = taskListItem.querySelector("button.delete");

     editButton.onclick = ();
     deleteButton.onclick = (deleteTask);
}

We now have a fully functioning delete button that calls a deleteTask function when pressed.  Wahoo!  Except......we havent't added all of this functionality to the addTask.  We want to make those buttons work when the listItem is created.  Call this function inside of the addTask, right before we reset the taskInput to "" and pass the listItem to the function.

makeButtonsWork(listItem);

On to the edit button.  When that button is pressed we need it to allow us to edit the task.  Looking at the HTML we see the existing list item that is in edit mode.  For that element, a class of "editMode" is inside of the <li> element:  <li class="editMode">.  That's a pretty good clue.  When we press the edit button we need to add that class and when we press it again, remove it.

As we think about this some more it's probably important to first check the class and see if it is in "editMode" if it is, route a should be taken, if not, route be.  Sounds like and if....else statement.  Let's see.

First, lets declare a variable with the function attached to it:

var editTask = function() {
}

Like the deleteTask function we need to capture the listItem in  a variable so we can act upon it.  Let's use this.parentNode again:

var editTask = function() {
     var listItem = this.parentNode;
}

The listItem has two properties we need to manipulate, the input element for text and the label element.  We need to be able to work with both:

var editTask = function() {
     var listItem = this.parentNode;

     var editableInput = listItem.querySelector("input[type=text]");
     var label = listItem.querySelector("label");
}

Now we need to make some sense out of the "editMode" class on the <li>.   After digging into the HTML, CSS and the developer tools in Google Chrome I think I finally made sense of it.  Bear with me here:

When we click on the edit mode we want the input[type=text] element to display and the label to hide.  When we are not in the edit mode the input[type=text] is hidden and only the label shows.

Said differently, if class="editMode" is not in the <li> we see the label.  When class="editMode" in in the <li> we see the input[type=text].  This was discerened from the different .css rules written.

We know, when a new listItem is created using the addTask function, the label has a value but the input[type=text] doesn't.  

So, in our editTask function we want to check and see, first, if the editMode class is present.  If it is we are seeing the input[type=text].  In this mode we set the label equal to the input[type = text].  Ifhe editTask class is not present we set the input[type]=text value equal to the label.

We can use a cool JavaScript method chain of .classList.contains("editMode') on the listItem variable.  Assigning that you a variable, called containsClass, will return either true or false.

In code:

var editTask = function() {
     var listItem = this.parentNode;

     var editableInput = listItem.querySelector("input[type=text]");
     var label = listItem.querySelector("label");

     var containsClass = listItem.classList.contains("editMode");

     if(containsClass) {     //this means if containsClass=true
        label.innerText = editableInput.value;
     }  else
        editableInput.value = label.innerText;
     }
}

Lastly, we have a method called toggle that we can call on the listItem and its classList passing the value of the class that we have been working with "editMode".

     listItem.classList.toggle("editMode");

The editTask code in totality:

var editTask = function() {
     var listItem = this.parentNode;

     var editableInput = listItem.querySelector("input[type=text]");
     var label = listItem.querySelector("label");

     var containsClass = listItem.classList.contains("editMode");

     if(containsClass) {     //this means if containsClass=true
        label.innerText = editableInput.value;
     }  else
        editableInput.value = label.innerText; 

     listItem.classList.toggle("editMode");
}

Remember to call this function back in the makeButtonsWork(taskListItem) function:


var makeButtonsWork = function(taskListItem) {

     var editButton = taskListItem.querySelector("button.edit");
     var deleteButton = taskListItem.querySelector("button.delete");

     editButton.onclick = (editTask);
     deleteButton.onclick = (deleteTask);
}


We should now have functional code that will allow us to add items, edit items and deletem items.  Pretty cool!

var taskInput = document.getElementById("new-task");
var addButton = document.getElementsByTagName("button")[0];
var incompleteTaskList = document.getElementById("incomplete-tasks");
var completedTaskList = document.getElementById("completed-tasks");

var createNewTaskElement = function(newTaskUserInput) {
     var listItem = document.createElement("li");
     var checkBox = document.createElement("input");
     var userInputLabel = document.createElement("label");
     var editableUserInput = document.createElement("input");
     var editButton = document.createElement("button");
     var deleteButton = document.createElement("button");

     checkBox.type = "checkbox";
     userInputLabel.type = "text";

     editButton.innerText = "Edit";
     editButton.className = "edit";
     deleteButton.innerText = "Delete";
     deleteButton.className = "delete";

     userInputLabel.innerText = newTaskUserInput;

     listItem.appendChild(checkBox);
     listItem.appendChild(userInputLabel);
     listItem.appendChild(editableUserInput);
     listItem.appendChild(editButton);
     listItem.appendChild(deleteButton);

     return listItem;

}

var addTask = function (){
     var listItem = createNewTaskElement(taskInput.value);
     incompleteTaskList.appendChild(listItem);

     makeButtonsWork(listItem);

     taskInput.value = "";
}

var deleteTask = function() {
     var listItem = this.parentNode;
     ul = listItem.parentNode

     ul.removeChild(listItem);
}

var editTask = function() {
     var listItem = this.parentNode;

     var editableInput = listItem.querySelector("input[type=text]");
     var label = listItem.querySelector("label");

     var containsClass = listItem.classList.contains("editMode");

     if(containsClass) {     //this means if containsClass=true
        label.innerText = editableInput.value;
     }  else
        editableInput.value = label.innerText;

     listItem.classList.toggle("editMode");
}

var makeButtonsWork = function(taskListItem) {

     var editButton = taskListItem.querySelector("button.edit");
     var deleteButton = taskListItem.querySelector("button.delete");

     editButton.onclick = (editTask);
     deleteButton.onclick = (deleteTask);
}

addButton.addEventListener("click", addTask);


We are in the home stretch!  All that's left is the checkbox and giving it some functionality.

We can follow the logic of the makeButtonsWork function but, in addition to the taskListItem we need to pass a formula that will mark the task as completed or incomplete and make some events happen like moving the item from one list to another.  Instead of onclick we can use onchange.

var makeCheckboxWork = function(taskListItem, completeOrIncomplete) {

     var checkBox =
     taskListItem.querySelector("input[type=checkbox]");

     checkBox.onchange = completeOrIncomplete;
}

Two new functions are necessary.  One for when the item is being marked complete and one for when the item is being marked incomplete.  Let's start with completed items.  We again create a variable for the ListItem equal to the parent element of the button.  We can then append this listItem to as a child to the completedTaskList (defined at the very beginning of this exercise).  Lastly, we need to call the makeCheckboxWork function and pass both the taskListItem and another function, taskIncomplete which we will write next.

var taskCompleted = function() {
     var listItem = this.parentNode;
     completedTaskList.appendChild(listItem);

     makeCheckboxWork(listItem, taskIncomplete);
}

The opposite of taskCompleted is taskIncomplete.  It's nearly identical except that we are appending to the incompleteTaskList and calling the taskComplete function inside of the makeCheckboxWork:

var taskIncomplete = function() {
     var listItem = this.parentNode;
     incompleteTaskList.appendChild(listItem);

     makeCheckboxWork(listItem, taskComplete);
}

Now, the very last thing to do is activate the checkboxes when the listItem is created.  Back inside of the addTask call the makeCheckboxWork.  

And that should complete our code!  Now, a missing element - the placeholder <li> elements have no functionality.  You can cycle over those items and make the buttons and checkbox work.

A summary of the code, in it's entirity:


var taskInput = document.getElementById("new-task");
var addButton = document.getElementsByTagName("button")[0];
var incompleteTaskList = document.getElementById("incomplete-tasks");
var completedTaskList = document.getElementById("completed-tasks");

var createNewTaskElement = function(newTaskUserInput) {
     var listItem = document.createElement("li");
     var checkBox = document.createElement("input");
     var userInputLabel = document.createElement("label");
     var editableUserInput = document.createElement("input");
     var editButton = document.createElement("button");
     var deleteButton = document.createElement("button");

     checkBox.type = "checkbox";
     editableUserInput.type = "text";

     editButton.innerText = "Edit";
     editButton.className = "edit";
     deleteButton.innerText = "Delete";
     deleteButton.className = "delete";

     userInputLabel.innerText = newTaskUserInput;

     listItem.appendChild(checkBox);
     listItem.appendChild(userInputLabel);
     listItem.appendChild(editableUserInput);
     listItem.appendChild(editButton);
     listItem.appendChild(deleteButton);

     return listItem;

}

var addTask = function (){
     var listItem = createNewTaskElement(taskInput.value);
     incompleteTaskList.appendChild(listItem);
  
    makeButtonsWork(listItem);
    makeCheckboxWork(listItem, taskCompleted);

     taskInput.value = "";
}

var deleteTask = function() {
     var listItem = this.parentNode;
     ul = listItem.parentNode

     ul.removeChild(listItem);
}

var editTask = function() {
     var listItem = this.parentNode;

     var editableInput = listItem.querySelector("input[type=text]");
     var label = listItem.querySelector("label");

     var containsClass = listItem.classList.contains("editMode");

     if(containsClass) {     //this means if containsClass=true
        label.innerText = editableInput.value;
     }  else
        editableInput.value = label.innerText;

     listItem.classList.toggle("editMode");
}

var makeButtonsWork = function(taskListItem) {

     var editButton = taskListItem.querySelector("button.edit");
     var deleteButton = taskListItem.querySelector("button.delete");

     editButton.onclick = (editTask);
     deleteButton.onclick = (deleteTask);
}

var taskCompleted = function() {
     var listItem = this.parentNode;
     completedTaskList.appendChild(listItem);

    makeCheckboxWork(listItem, taskIncomplete);
}

var taskIncomplete = function() {
     var listItem = this.parentNode;
     incompleteTaskList.appendChild(listItem);

     makeCheckboxWork(listItem, taskCompleted);
}

var makeCheckboxWork = function(taskListItem, completeOrIncomplete) {

     var checkBox = taskListItem.querySelector("input[type=checkbox]");

     checkBox.onchange = completeOrIncomplete;
}

addButton.addEventListener("click", addTask);

I hope that helped you as much as it helped me.  This was a difficult challenge and I had to approach it a bit differently than the lesson itself.  But I think I get it now.  And I at least have a place where I can reference back as I continue down this exciting path.  

Cheers!