JavaScript30 - Day 15

| Comments

Day 15 of #javascript30 is about localStorage and event delegation.

LocalStorage

The Window.localStorage a simple key-value store that lives in the browser. It is pr. site and has a nice and simple interface. Keep in mind that both the key and the value should be a string, so often times the value is made into json if it is an object.

The methods on the Storage object can be found here, but I'll just list the most important ones:

That's it! Nice and simple way to talk to a key-value store. Oh, there is also Storage.clear() for clearing the whole storehouse.

Event Delegation

Is basically putting an event listener on a parent element to avoid having to attach listeners when new items are added dynamically. When you want to have listeners on something that doesn't exist yet or maybe just instead of attaching 1000 listeners you can put the listener on the parent element. When the parent element is clicked for instance, you then look at the event to see if an item you want to listen for was the target.

function myEventHandler(e) {
  if (!e.target.matches('input')) {
    // Not what we are listening for, so return.
    return;
  }
  // An input element was clicked. Do what needs to be done.
}

preventDefault() confusion

This has had me confused. There is an Event.preventDefault in vanilla JS and then jQuery has event.preventDefault() with the exact same name. Just something to keep in mind to avoid confusion.


JavaScript30 - Day 14

| Comments

Day 14 of #javascript30 is understanding reference vs copy on variables.

This is where so many programmers make mistakes. Not just in JavaScript, but in most programming languages. Understanding — as in really understanding this is super important.

JavaScript and PHP are almost the same when it comes to refrence vs copy, with the exception of arrays. JavaScript assigns them by reference, and PHP doesn't. Strings, numbers and booleans are copies. Everything else (I think?) is reference.

Cloning arrays

The problem is that we can overwrite the original array when we edit the array that is pointing to it. Here is an example with tapirs, because they always make the best examples:

const newWorldTapirTypes = ["Baird's", 'Brazilian', 'Rabbit', 'Mountain'];

// Make an array with not just New World tapirs.
const allTapirs = newWorldTapirTypes;
allTapirs.push('Malayan');

console.dir(newWorldTapirTypes);
console.dir(allTapirs);
// There is now a wrong tapir in the new world types.
// [ 'Baird\'s', 'Brazilian', 'Rabbit', 'Mountain', 'Malayan' ]
// [ 'Baird\'s', 'Brazilian', 'Rabbit', 'Mountain', 'Malayan' ]

Here is how to get a copy instead of a reference: Use slice():

const newWorldTapirTypes = ["Baird's", 'Brazilian', 'Rabbit', 'Mountain'];

// Use slice with no args
const allTapirs = newWorldTapirTypes.slice();
allTapirs.push('Malayan');

console.dir(newWorldTapirTypes);
console.dir(allTapirs);
// [ 'Baird\'s', 'Brazilian', 'Rabbit', 'Mountain' ]
// [ 'Baird\'s', 'Brazilian', 'Rabbit', 'Mountain', 'Malayan' ]

Use concat():

const newWorldTapirTypes = ["Baird's", 'Brazilian', 'Rabbit', 'Mountain'];

// Use slice with no args
const allTapirs = [].concat(newWorldTapirTypes);
allTapirs.push('Malayan');

console.dir(newWorldTapirTypes);
console.dir(allTapirs);
// [ 'Baird\'s', 'Brazilian', 'Rabbit', 'Mountain' ]
// [ 'Baird\'s', 'Brazilian', 'Rabbit', 'Mountain', 'Malayan' ]

Or use ES6's spread

const newWorldTapirTypes = ["Baird's", 'Brazilian', 'Rabbit', 'Mountain'];

// Use slice with no args
const allTapirs = [...newWorldTapirTypes];
allTapirs.push('Malayan');

console.dir(newWorldTapirTypes);
console.dir(allTapirs);
// [ 'Baird\'s', 'Brazilian', 'Rabbit', 'Mountain' ]
// [ 'Baird\'s', 'Brazilian', 'Rabbit', 'Mountain', 'Malayan' ]

Cloning objects

Same problem:

const tapir = {
  name: 'Timmy',
  age: 4,
  type: "Baird's",
  home: "Los Angeles"
}

const tapir2 = tapir;
tapir2.home = 'Copenhagen';

console.dir(tapir);
console.dir(tapir2);
// Taprir 1 does not live in Copenhagen!
// { name: 'Timmy', age: 4, type: 'Baird\'s', home: 'Copenhagen' }
// { name: 'Timmy', age: 4, type: 'Baird\'s', home: 'Copenhagen' }

How to go about this?

Use Object.assign

const tapir = {
  name: 'Timmy',
  age: 4,
  type: "Baird's",
  home: "Los Angeles"
}

const tapir2 = Object.assign({}, tapir, { home: "Copenhagen" });
tapir2.home = 'Copenhagen';

console.dir(tapir);
console.dir(tapir2);
// { name: 'Timmy', age: 4, type: 'Baird\'s', home: 'Los Angeles' }
// { name: 'Timmy', age: 4, type: 'Baird\'s', home: 'Copenhagen' }

Note that that way of "cloning" is not recursive. If you need that, then first think about why you need that. There might be a better way around it. But if you really need it — then use this "poor man's clone":

const tapir = {
  name: 'Timmy',
  age: 4,
  type: "Baird's",
  home: {
    country: 'USA',
    city: 'Los Angeles'
  }
}

const tapir2 = JSON.parse(JSON.stringify(tapir));

tapir2.home = { country: "Denmark", city: "Copenhagen" };

console.dir(tapir);
console.dir(tapir2);
// { name: 'Timmy',
//  age: 4,
//  type: 'Baird\'s',
//   home: { country: 'USA', city: 'Los Angeles' } }
// { name: 'Timmy',
//  age: 4,
//  type: 'Baird\'s',
//  home: { country: 'Denmark', city: 'Copenhagen' } }

JavaScript30 - Day 13

| Comments

Day 13 of #javascript30 is a how to get those swishy images come fly in from the sides of the page.

Like Wes, I am not a big fan of animations that do this, but I still get requests for stuff like this from time to time. So I guess it is good to know.

In the excercise, a function is called through a debounce function on scroll. I have used a function like that in the past with underscore.js and it is a really good idea if you don't want to slam the browser with stuff on scroll.

window.scrollY

The window.scrollY property gives you the number of pixels that has been scrolled down. I like to think it of it as "number of pixels that i have scrolled out of view from the top". Note that it may or may not be an integer. Modern browsers will give subpixel values.

HTMLElement.offsetTop

The HTMLElement.offsetTop property will give you the number of pixels from the element you are on to the closest parent element that is relatively positioned. This is a little tricky and it really is just easiest to console.log the value and see for yourself before you start doing math.

Math with the two properties could be like the example below, where we find the position on the page of the middle of an element.

  const slideInAt = (window.scrollY + window.innerHeight) - image.height / 2;

JavaScript30 - Day 12

| Comments

Day 12 of #javascript30 is having fun with konami codes or whatever you want to be your secret code.

This lesson didn't introduce things that surprised me, but it had a very nice and precise use of .splice() that illustrates how it mutates the array instead of returning a new one. It is basically practicing what we learned in lesson 7 only it is slicing the array backwards. And you never become too good of a programmer to see examples using arrays.

Also. Many points for using the legendary Cornify!


JavaScript30 - Day 11

| Comments

Day 11 of #javascript30 is dealing with controlling a video player.

Like with the <audio> element, I was amazed at how simple the interaction with the player is in the <video> element. I learned how to control volume, skipping and the progress bar. Neat! Once again it blew my mind how many events you can listen for. Here is a list of media events and listening on them instead of trying to keep your own state for example the play button makes a ton of sense.

Flexbox was used in the lesson to control the controls and the super handy flex-basis was used for the progress bar in percent. Simple and elegant.

For the sliders for volume and playback rate, the function used in the lesson used the name of the event it was listening for to set the value. Sorta like this:

video[this.name] = this.value;

Where this is the event. The name of the events in this case was either playbackRate or volume, so it the could also be written like this:

if (this.name === 'volume') {
    video.volume = this.value;
}
else if (this.name === 'playbackRate') {
    video.volume = this.value;
}

.. but the first piece of code is much easier to use and understand once you get over the initial confusion thinking — wait.. Is video an array?. Obviously this will only work if the event names and the property names are the same.


JavaScript30 - Day 10

| Comments

Day 10 of #javascript30 seems simple enough. Check multiple checkboxes when holding shift down. It was pretty hard, though.

This one is a prime example of why only practice makes you a good JavaScript programmer. It's a pretty common UI thing - most email clients use it, so really good call by Wes to include this one in the lessons. I find that in JavaScript there is a lot of UI interaction that I am so bad at because I don't use it in PHP and therefore I just haven't done it enough. And that makes a pretty simple task like this one into something that takes a good while for me to figure out. Should I keep a boolean with state? Should I use slice on the array instead of looping over the whole thing? I simply don't know best practices or pros/cons for these things. This solution loops over the array of checkboxes and holds a boolean if we are in between lasted checked box and the box currently being checked.

There was only one language concept I didn't know that got introduced in this one. And that was that there is a shiftKey property on the MouseEvent. My first implementation had listeners on the keyup and down on the shift key, and it was kinda messy.


JavaScript30 - Day 9

| Comments

Day 9 of #javascript30 is about what you can do with the browser's dev tools to help you in your code.

Inside console.log you can interpolate strings like in PHP's sprintf(). You can also just use the backticks from ES6.

There are a number of methods you can call on the Console object - it's worth checking that list out. One in particular I think is cool: console.assert(). It only prints if what is inside the parentheses evaluates to true. While developing it could be a good idea to use that for some assumptions you have. If the console stays quiet, you're assuming right!

const date = new Date();
// Nothing is in the console.
console.assert(date.getFullYear === date.getFullYear, `Dates don't match`);
// See screenshot below.
console.assert(date.getFullYear !== date.getFullYear, `Dates don't match`);

console.assert()


JavaScript30 - Day 8

| Comments

Day 8 of #javascript30 is playing around with canvas and drawing colorful things in the browser.

You don't actually draw on the the <canvas> - you draw on the 'context' that you get from the canvas - which can be 2D or 3D. Todays lesson is focusing on 2D and you can do a ton of cool stuff with that, so go check out the list of functions that CanvasRenderingContext2D offers. Lines, shapes, text and so on.

What made todays lesson super cool is the use of a variable that is used to increment the hue on the lines we are drawing by setting its strokeStyle. This made it so that I was not just drawing a monochrome line, but a line in all the colors of the rainbow. UNICORN BOWELS!

Unicorn Bowels

Destructuring assignment

In JavaScript the equivalent to PHP's list() is the destructuring assignment. So the idea is that you can assign values to variables from an array like so:

var a, b, rest;
[a, b] = [10, 20];
console.log(a); // 10
console.log(b); // 20

I'm not a big fan of that syntax. Not in PHP either. I have yet to see a case where it makes a ton of sense.


JavaScript30 - Day 7

| Comments

Day 7 of #javascript30 is another array workout. I learn about some() and every() and feel good about it.

Arrays, chapter II

This time we are doing more filtering and searching in arrays. Some of the filtering uses Date — an old nemesis of mine. It's actually OK simple to use and I learned that with parentheses you can call functions on an object like this:

const year = ((new Date()).getFullYear();

rather than:

const date = new Date();
const year = date.getFullYear();

some()

some() will check if any item in the array passes some condition. It returns true if any item passes the condition.

every()

every() is a lot like some(), only this one returns true only id all items pass a condition.

splice()

splice() is a little tricky to understand because it can do many different things depending on what arguments you pass it. One thing that is super important to note, is that it mutates the array it is called on. MDN has a list of array functions that modify the array that is worth reading and understanding.

A couple of examples. Note that the array is modified!

const tapirTypes = ["Baird's", 'Malayan', 'Brazilian', 'Rabbit', 'Mountain'];
// Remove the animal that is not a tapir.
tapirTypes.splice(3, 1);
console.log(tapirTypes);
// [ "Baird's", "Malayan", "Brazilian", "Mountain" ]

// Add the newest discovered tapir to index 1.
tapirTypes.splice(1, 0, 'Kabomani');
console.log(tapirTypes);
// [ "Baird's", "Kabomani", "Malayan", "Brazilian", "Mountain" ]

find()

find() will search your array with the callback function you give it, and return the value of the first element that meets the callback function's test criteria. If nothing is found that satisfies the criteria, it returns undefined.

findIndex()

findIndex() is just like find(), but it returns the index of the item that satisfied the callback function test criteria. If nothing was found it returns -1.


JavaScript30 - Day 6

| Comments

Day 6 of #javascript30 is an ajax type ahead form. I learned about the fetch API, promises, asynchronicity, and I might finally understand 'const'.

Fetch API

In the past I have used jQuery's ajax(), get(), or getJSON() for requesting stuff or I have gone to Stack Overflow and copied an XMLHttpRequest like the one below because there was no way I'd remember how to do that from time to time.

var xmlHttp = new XMLHttpRequest();
  xmlHttp.onreadystatechange = function() {
    if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
      console.log(xmlHttp.responseText);
  }
  xmlHttp.open("GET", endpoint, true); // true for asynchronous
  xmlHttp.send(null);

It so much easier to use fetch():

let cities = [];

fetch(endpoint)
  .then(blob => blob.json())
  .then(data => cities.push(...data));

Promises

fetch() returns a Promise object and when the data has been fetched at some point, the code in the then()s is executed. It is an asynchronous call, so it will be executed at some point. This is important and I actually ran into some problems with console.loging my results. My cities array was empty, but if I console.loged the data inside the call to then(), I had the array filled? It was a good learning experience to run into because I realized the asynchronousness (is that a word?) of the promises. I went to Stack Overflow and copied a function that would "sleep" for 2 seconds and then I could console.log my array of cities. Slow internet connection made me understand promises better. Thanks, wonky internet connection!

const finally clicked for me

I finally understood what const is in JavaScript. It is not the same as a constant in PHP, where the value cannot change, so they were a little confusing to me. I went and read the documentation and this part sums it up nicely:

The const declaration creates a read-only reference to a value. It does not mean the value it holds is immutable, just that the variable identifier cannot be reassigned. For instance, in the case where the content is an object, this means the object's contents (e.g. its parameters) can be altered.

So you can change a const, just not reassign it.

RTFM

This lesson taught me a lot and I took my time reading the documentation for the things I was using. It can hardly surprise anybody that reading the manual is a good idea, but I am actually pleasantly surprised at how good and very readable the docs on MDN (Mozilla Developer Network) are. So if you are also trying to learn more JavaScript, I really recommend spending some time just reading about what you are learning instead of what I normally do which is just hunting for something to copy paste.