Jeremy Troy Suchanski

Web Frontend Development II

Gary James

April 20, 2022

L02 Reading Notes

Master Mobile UX (Notes)

“If you design for the smallest, least powerful device first, then you will focus on the content, and your core functionality. That leads to simple, beautiful apps. If you do it the other way around, it’ll be like trying to put a marshmallow into a piggybank, which is neither simple nor beautiful.” Joel Marsh – the Hipper Element

Design paradigm was design for desktops (which reach its peak in 30 years in 2011)

2007 iPhone was introduced by Steve Jobs, saw a rise in 2 years, 2009 and the by 2011 mobile usage started to skyrocket & after 2011 it exploded. This began a design paradigm shift with a huge range of devices with different screen sizes and resolutions for the design.

“The simple guideline is whatever you are doing – do mobile first,” Eric Schmidt, World Mobile Congress Keynote 2012

“It’s really hard to design products by focus groups. A lot of times, people don’t know what they want until you show it to them.” Steve Jobs – Apple

To ensure things are loading quickly for our users:

#1 reduce image use throughout our site & apps

#2 use SVG & Webfonts for images = lower bandwidth

#3 optimize and minimize CSS & JS files

#4 GZIP files to remove unnecessary data (compressing files reducing load time)

“The trouble with software is . . . it sucks.” Stewart Alsop – Venture Capitalist

#1 make sure the most commonly used menu items are located at the bottom in easiest reachable area. Benefits: (#1 this creates a perception of speed #2 improved engagement rates #3 increased user satisfaction)

#2 perception matters make things as obvious as possible (example, Hamburger menu 3 lines – link: with just word menu = -22.2% less clicks; 3 line symbol with word menu in outlined box = +5.7% more clicks; just word menu in outlined box = +20% more clicks)

“[The constraints of Mobile] push you toward more focused simplified solutions. You can only fit so much on the screen . . . Mobile is a great forcing function for simplicity.” Luke Wroblewski – Co-Founder of Polar and Bagcheck

#1 minimize inputs by only having required inputs/ reducing unnecessary input lines

#2 break larger forms into sections with a progress indicator – it’s less overwhelming

#3 enable autocomplete – Benefits (#1 greatly reduces time required to fill in form #2 reduces errors #3 helps to increase conversions)

#4 passwords – show passwords by default instead of masking (but allow users to hide them easily if they need to do so) “Typically, masking passwords doesn’t even increase security, but it does cost you business due to log in failures.” -Nielson Norman Group

#5 credit card information – make it as simple & intuitive as possible, auto advance user through the process & provide simple visual indicators (space credit card numbers automatically) of what’s being entered (example: 1 line entry field by Brad Frost)

#6 keyboard – provide best one for input being entered (when taking numbers, ensure the number keypad is used on Mobile)

#7 phone numbers – linking phone numbers on your website using an <a> tag (example: <a href=tel:14035551234>403-555-1234</a>) makes it much easier for people to call from mobile devices (don’t force a copy & paste)

“End users, not technologies, shape the market. [So] marketers need to stay abreast not only of technological developments but also of the way people respond to them.” Matt Haig – Author

Ch12: Object-Oriented Programming in JavaScript (Notes)

Object Oriented Programming (OOP): separating code into objects having properties & methods

Example of creating object with class declaration

class Dice {

    constructor(sides=6) {

        this.sides = sides;

    }

 

    roll() {

        return Math.floor(this.sides * Math.random() + 1)

    }

}

Example of creating an instance of that class declaration object:

const blueDice = new Dice(20);

Example:

const greenDice = new redDice.constructor(10);

Example:

static description() {

        return 'A way of choosing random numbers'

    }

Static methods are not available to instances of the class only to the actual class name.

Example of the method being called:

Dice.description()

Example of new property through the prototype:

Turtle.prototype.weapon = 'Hands';

Example of new method through the prototype:

Turtle.prototype.attack = function(){

return `Feel the power of my ${this.weapon}!`;

}

Example:

class NinjaTurtle extends Turtle {

    constructor(name) {

        super(name);

        this.weapon = 'hands';

    }

    attack() { return `Feel the power of my ${this.weapon}!` } 

}

Example of adding a method:

Number.prototype.isEven = function() {

    return this%2 === 0;

}

An alternative way to avoid causing problems is to use extends to subclass a built class and create your own class. It has the advantage of not interfering with the built-in array class at all. For example, you could create your own array class by extending the built in array class, like so:

class myArray extends Array {

    constructor(...args){

        super(...args);

    }

    delete(i) {

        return this.splice(i,1);

    }

}

Object.getOwnPropertyDescriptor() method allows us to see the property descriptors

Object.defineProperty() method allows us to add properties to an object. Any attributes left out will take the default values.

Example of creating an instance using Object.create() method:

const lois = Object.create(Human);

Every time a new object is created using the Object.create() method, the new object inherits all the properties and methods from the parent object, which becomes the new object’s prototype. (The instanceof operator will not work when objects have been created this way. It only works when using constructor functions to create objects.)

Object.assign() method provides the basic mixin functionality. This will assign to the object provided as the first argument all of the properties from any objects provided as further arguments. Issue = (If any of the properties being mixed in are arrays or nested objects, only a shallow copy is made.) To avoid a shallow copy, create your own mixin() function that will assign all properties of an object to another object as a deep copy:

function mixin(target,...objects) {

    for (const object of objects) {   

    if(typeof object === 'object') {

        for (const key of Object.keys(object)) {

            if (typeof object[key] === 'object') {

            target[key] = Array.isArray(object[key]) ? [] : {};

            mixin(target[key],object[key]);

            } else {

            Object.assign(target,object);  

            }

        }

        }

    }

    return target;

}

function copy(target) {

    const object =  Object.create(Object.getPrototypeOf(target));

    mixin(object,target);

    return object;

}

Example of factory function:

function createSuperhuman(...mixins) {

    const object = copy(Superhuman);

    return mixin(object,...mixins);

}

#1 Use that = this:

superman.findFriends = function(){

    const that = this;

    this.friends.forEach(function(friend) {

        console.log(`${friend.name} is friends with ${that.name}`);

    }

    );

}

#2 Use bind(this). The bind() method is a method for all functions and is used to set the value of this in the function:

superman.findFriends = function() {

    this.friends.forEach(function(friend) {

        console.log(`${friend.name} is friends with ${this.name}`);

    }.bind(this);)

}

#3 Use for-of Instead Of forEach(). ES6 introduced the for-of syntax for arrays and this does not require a nested function to be used thus avoiding the problem. superman.findFriends = function() {

    for(const friend of this.friends) {

        console.log(`${friend.name} is friends with ${this.name}`);

    };

}

#4 Use Arrow Functions. Arrow functions were introduced in ES6, and one of the advantages of using them is that they don't have their own this context, so this remains bound to the original object making the function call.

superman.findFriends = function() {

    this.friends.forEach((friend) => {

        console.log(`${friend.name} is friends with ${this.name}`);

    }

    );

}

Example of borrowing a method from a prototype:

const fly = superman.fly;

fly.call(batman)

#2 The method can also be borrowed directly from an array literal.

Example

[].slice.call(arguments, 1, 3)

#3 An array-like object can effectively be turned into an array using the slice() method with no arguments. This will return the arguments object as an array (since the slice() method returns an array):

Example:

const argumentsArray = Array.prototype.slice.call(arguments);

#4 From ES6 onwards the Array.from() method can be used to turn an array-like object into an array.

Example:

const argumentsArray = Array.from(arguments);

#5 The spread operator can be used to easily turn an array-like object into an array.

Example:

const argumentsArray = [...arguments];

Good practices: #1 If you do decide to use classes, it’s recommended to make them 'skinny' ― meaning they don't have too many properties and methods. #2 When creating classes is to keep inheritance chains short. A good rule of thumb is to only inherit once, keeping the inheritance chain to just two objects makes unpicking any issues far easier. #3 If you want to use a particular method from a class, but it has lots of properties and methods you don't need, then it would be preferable to just 'borrow' the method instead. #4 An even better approach would be to move the banana() method into a separate object then add it as a mixin to the Gorilla class, and any other objects that required it. 

 

 

Object methods, "this" (Notes)

Actions are represented in JavaScript by functions in properties. A function that is a property of an object is called its method.

The value of this is the object “before dot”, the one used to call the method. It can be used in any function, even if it’s not a method of an object. Calling without an object: this == undefined. In JavaScript this is “free”, its value is evaluated at call-time and does not depend on where the method was declared, but rather on what object is “before the dot”.

Arrow functions are special: they don’t have their “own” this. If we reference this from such a function, it’s taken from the outer “normal” function.

Ch15: Modern JavaScript (Notes)

Default Exports: Refers to a single variable, function or class in a module that can be imported without having to be explicitly named & is done with keywords export default. Having more than one default export will result in a syntax error. For importing - the big difference with default exports is that you don't need to use curly braces or make any mention of the value that is being imported (example: import PI from './pi.js';) The alias that is assigned to the imported module does not have to match its name in the actual module, you can change it. (example: import square function as sq)

Coupling: The coupling of code refers to how dependent certain elements or modules of code are on each other. 

Tightly coupled: If one relies on the other to run. This often occurs if a piece of code makes hard-coded references to another piece of code, requiring it to be used. This will often mean that changes to one piece of code will necessitate changes in the other.

Loosely coupled: If one piece of code can be easily substituted by another without affecting the final outcome. This is often achieved by referring to common methods that are shared by the alternative modules.

Good design: Keep code as loosely coupled as possible.

MV*: It is quite common to see the acronym MV* used as a catch-all term to describe JavaScript frameworks, rather than MVC. This is because many JavaScript implementations do not strictly follow the controller pattern. Sometimes controller code is mixed into the views, and sometimes other patterns are used, such as Model-View-Presenter (MVP), Model-View-ViewModel (MVVM), and AngularJS, which calls itself a Model-View-Whatever (MVW) framework.

Persistence: Most web applications will need some form of persistence to save the information held in the models in order to maintain state. (examples: #1 Web Storage API #2 send a JSON representation of the model to a back-end database using Ajax whenever a model changes)

Data Binding: the process of linking the model and view together. One-way data binding is when a change in the model will automatically result in the view being updated. Two-way data binding is when a change in the view automatically updates the model.

Mechanism for inserting dynamic data into the HTML: #1 placing dynamic code inside curly braces (the 'mustache' symbol – example: <h1>Hello {{ name }}</h1>) #2 placing dynamic code inside the special <% %> tags (made popular by Embedded Ruby “ERB” – example: <h1>Hello <%= name %></h1>)

Popular templating languages: #1 Handlebars #2 Pug #3 EJS #4 Mustache #5 Nunjucks

Web Components: The Polymer Project (https://www.polymer-project.org/) is a JavaScript library that attempts to implement web components.

‘View’ Libraries: They are worth considering if you find your user interface is starting to become more complicated or has to react quickly to changes in data. They deal with just the view layer of MVC. They have the single goal of making it easier to update the user interface while also keeping what is displayed synchronized with underlying data that it’s representing. Most popular two view libraries: #1 React (developed by Facebook) #2 Vue.js (developed by Evan You after he worked on AngularJS at Google) Other popular alternatives that achieve the single goal in a slightly different way: #1 Svelte #2 Inferno #3 Deku #4 Preact #5 Virtual-DOM #6 Moon

Virtual DOM: React and Vue.js both use the concept to speed up the process of updating HTML pages. Instead of directly changing the DOM a virtual DOM is updated in memory. Reconciliation: It makes rendering web pages extremely fast. The real DOM is only updated when it needs to be & is done by comparing differences between the virtual DOM and actual DOM & only updating those elements that have actually changed.

Components: concept to represent visual elements. You can create multiple components that can then be reused in other projects as well as being combined to make larger components. Each component is entirely self-contained, with all the HTML, CSS and JavaScript being defined inside the component itself. 

Package: is a directory with one or more files of code, as well as a package.json file that contains information about the code in the package.

Searching for Packages: use the search command. (example: npm search test - returns a list of packages) You can also search for a package on the npm website (https://www.npmjs.com/).

npm init command:  used when you start a project. It walks you through creating a package.json file.

Installing Packages Locally: the install command is used. Adding the --save flag will ensure the package.json file is updated with the relevant information about the package.

            Example: npm install --save lodash

devDependency: packages that aren't required by the application itself but are used in development to complete tasks to do with the application (example: running tests or transpiling the code). Any packages installed as a devDependency will not be packaged in the final build of an application that is deployed.

Example: npm install --save-dev jest

Installing a specific version: place the @ symbol after the package name, followed by the version you require. (It is usually best to just install the latest version of a package, although there may be times when you need to install a specific version of a package so it’s compatible with other packages.)

Semantic Versioning: the first number refers to the major version number, the second is the minor version number, and the third number is for patches.

            Example: npm install --save-dev jest@18.1.0

The package.json File: create a package.json file inside a directory by running the npm init command. It manages any packages that are installed locally. It keeps track on which packages are installed, and what dependencies they rely on. It also allows you to specify specific versions of packages to use. They require: #1 “name” (all in lowercase and containing no spaces) #2 “version” (uses semantic versioning of the form major.minor.patch)

By-passing the questions on install of a package.json File: use the --yes or -y flag

            Example: npm install –yes

Setting defaults: use npm set command:

            Example: npm set init.author.email "daz@sitepoint.com"

     npm set init.author.name "DAZ"

      npm set init.license "MIT"

README: Most projects will have a file in the root directory called 'README'. This files serves as the application's documentation and contains information about it. If you leave the description field blank, then npm will use the first line of the README file as the description.

Installing all the packages that are required to run the application: the package.json file is that it contains all the information about a project's dependencies. So, from inside the root directory of the project simply entering the npm install command will download and install all the necessary packages, as well as any dependencies.

Installing Globally: There are some cases, when it makes sense to install a package globally. This is usually when the package is to be used on a system-wide basis, rather than just for a specific project. Then they’re available system-wide from any directory on your machine. (it isn't usually recommended, as the package files are not saved with the project [they are installed in the system folders on your machine] and there will be no reference to the package as a dependency in the package.json file.)To install a package globally, the global flag needs to be added to the install command (example: npm install --global lodash)

Installing Globally & Locally: You might want it available on your system so you can have quick access to the library when you’re experimenting with code in the Node REPL, but you might also want it as a specific dependency in a web application you build. There is nothing wrong with this, it just means the files exist in two (or more) different places on your machine.

Permissions: You need to be careful about permissions when installing packages globally. When you install globally you are sometimes required to install the package into a system directory that you don't have permission to access. Three ways to fix this problem are explained on the npm website.

Listing Installed Packages: You can list all the packages that have been installed locally using the list command. (example: npm list) If you only want to see the packages that you have installed directly, you can use the depth flag, with a value of 0. (example: npm list --depth=0) You can find out which packages have been installed globally by adding the --global or -g flag. (example: npm list –global) 

Updating A Package: You can find out if any of the packages you have installed have been updated by running the command outdated. (examples: #1 locally - npm outdated #2 globally - npm outdated -g) This will display a list of any packages that are listed in package.json and have a more recent version available than what’s currently installed. npm makes it easy to update a package to the latest version using the update command. (examples: #1 jest package - npm update jest #2 all packages - npm update #3 globally - npm update --global) 

Uninstalling Packages: You can use npm to uninstall a package using the uninstall command (example: npm uninstall lodash) To remove it from the dependencies in package.json, you will need to use the save flag (example: npm uninstall --save lodash) If it was installed as a devDependency, you will need to add the --save-dev flag to remove it from the package.json file (example: npm uninstall --save-dev jest) Global packages can be uninstalled by adding the --global or -g to the uninstall command (example: npm uninstall --global lodash)

npm Command Aliases: #1 install = i #2 uninstall = un #3 update = up #4 list = ls

Good practice: use well-commented and descriptive code in development and minified code in production.

Root Files: index.html is traditionally the page that a web browser will load by default. For small projects it is usually okay to put all the JavaScript into a single file in the root directory. The file is usually called #1 ‘main.js’ #2 'index.js' #3 'app.js' #4 something similar. As a project gets bigger, you might want to move all of your JavaScript into a separate folder, called #1 'js' #2 'scripts' #3 something similar. In a similar way, it is customary to have a single CSS file in the root directory or keep multiple CSS files saved in a folder called #1 'CSS' #2 'stylesheets' #3 something similar.

src folder: Code that needs to be transpiled is often placed inside a folder called src (short for 'source'). The transpiler can then compile the JavaScript code into a single file in a different location (usually this is either the root directory or the 'dist' folder).

dist folder: The output of any transpiled code from the src folder is usually placed inside a folder called 'dist' (short for 'distribution'). Even though there might be many different files inside the src folder, the result of transpilation means there is usually only a single JavaScript file inside the 'dist' folder, usually called budle.js or 'bundle.min.js' if it has also been minified.

lib folder: Any modules are usually stored in a folder called lib (short for 'library') that is usually placed inside the src folder. Third-party libraries are often placed inside a folder called vendor, although they are sometimes also placed inside the lib folder.

node_modules: Any third-party code installed using npm will be installed into this directory/folder.

Modules: For only a small number of modules, you can just keep them all in the root of the lib directory, but as the size of your application grows, you might need to start thinking about how to organize the modules into separate folders.

Organization By Type: has folders for the different types of file that will be used in your project such as Javascript, CSS, Images and Tests.

            Example:

Modular Organization: groups files by feature, so all the files associated with a certain feature together in a single folder. Advantages: #1 keeps all the code related to that particular feature all in one place #2 modules can grow to incorporate their own file structure and sub-directories as required #3 allows developers to work on a specific feature without affecting another feature (particularly useful when working in teams) #4 modular nature of this approach makes it easy to switch one feature for another, without affecting the rest of the project #5 each module is self-contained, making them easy to test.

            Example

Start Small: Start small with just the root files: index.html, main.js and main.css. Then expand as the project grows.

Loaders: a concept Webpack uses, which allows you to perform tasks such as #1 Transpiling code #2 Minifying code #3 Linting code #4 Testing code #5 Compiling CSS preprocessor files (such as Sass, Less or Stylus) into standard CSS files. #6 Compile multiple CSS and image files into a single destination folder.

Webpack Example

#1 create a new folder called “webpack-example”, then navigate to that folder in the terminal and create a package.json file with the command (npm init -y)

#2 use npm to install Webpack as part of the development process with the command (npm install --save-dev webpack)

#3 create a simple HTML file called webpack.html that includes the following code:

<!doctype html>

<html lang='en'>

<head>

    <meta charset='utf-8'>

     <title>Webpack Example</title>

</head>

<body>

    <script src='bundle.js'></script>

</body>

</html>

#4 create a JavaScript file called main.js that contains the following code:

import _ from 'lodash';

console.log(_.now());

#5 On its own, this code won't work, but we can use Webpack to sort out importing the Lodash library from the 'node_modules' directory. To do this, we simply need to run the following command in the terminal:

./node_modules/.bin/webpack main.js bundle.js

The first argument is the file that contains our code ('main.js), and the second argument is the file that we want the output to be written to (bundle.js).

#6 To make things simpler, going forward, we can create a webpack configuration file called 'webpack.config.js'. This is actually just a standard JavaScript file and should be saved in the root of the directory and contain the following code:

module.exports = {

    entry: './main.js',

    output: {

         filename: 'bundle.js',

        path: __dirname

    }

};

#7 This can be run using the following line of code in the terminal:

./node_modules/.bin/webpack

#8 use npm to simplify this process by updating the "scripts" property in your package.json file to the following:

"scripts": {

     "test": "echo \"Error: no test specified\" && exit 1",

     "build": "webpack"

}

#8 Now you can run Webpack using the following command:

npm run build