Long time no see philosophical space pepper!

: Clippy, bash / zsh commands, CSS Color

Support/feature testing in JS

Method/Property

if ("method" in object) {...}

// Example (from MDN):
if("geolocation" in navigator) { ... }

Feature test

(from modernizr)

const hasSomeFeature = () => {
  try {
    // test something, eg. write to localStorage.
    return true;
  } catch(e) {
    return false;
  }
}

Check if element has property

Create an instance of the Element in memory and test the porperty

if (!!document.createElement('element').property) {...}

// Example (from MDN):
function supports_canvas() {
return !!document.createElement('canvas').getContext;
}

if(supports_canvas()) { ... }

Remidner: !! returns the truthy value (bool).

→ MDN

CSS Feature support

equivalent of CSS’s @supports(property: value).

CSS.supports("property", "value")

→ MDN

Media Query

equivalent of CSS’s @media

window.matchMedia('(max-width: 600px)');

check match with .matches (bool):

const mq = window.matchMedia('(max-width: 600px)');

// Checking for matches:
if (mq.matches) {
  // media query matches!
}

// Listening for matches:
mq.addEventListener('change', e => {
  if (e.matches) {
    // ...
  }
});

Careful: At the time of writing the addEventListener example does not work in Safari and IE, see browser compatibility table (MDN).

→ MDN, in depth: CSS Ttricks

Multiply conent for prototyping

A few strategies to «generate» (or rather multiply) content with JS.

repeat() existing content

Just copy/paste the contetn x times.

<div id="grid">
    <div class="item">
         ...
    </div>
</div>
const container = document.getElementById('grid');
container.insertAdjacentHTML('beforeend',container.innerHTML.repeat(3) );

populate <template />

Use template-tags and replace %placeholders% with data.

<template id="exampleTemplate">
    <section>
        <h1>%1%</h1>
        ...
    </section>
</template>

<div id="dynamicContent"></div>
const expandTemplate = (id, data) => {
	const template = document.querySelector(`#${id}`);
	const target = document.querySelector('#dynamicContent');
	
	data.forEach((dataPoint, index) => {
		const instance = document.importNode(template.content, true);
		
		// replace 
		const section = instance.querySelector('section');
		section.innerHTML = section.innerHTML.replace( '%1%', dataPoint ); 

		target.appendChild(instance);
	});

}

expandTemplate( 'exampleTemplate', ['Hello', 'World', 'Data'] );

<custom-HTML-tags /> + <template />

<template id="exampleTemplate">
    <section>
        ...
    </section>
</template>

<custom-section foo="bar"></custom-section>
<custom-section foo="doo"></custom-section>
class CustomSection extends HTMLElement {
    connectedCallback() {
		
        if (this.hasAttribute('foo')) {
            const fooAtt = this.getAttribute('foo');
        }
		
        // use template
        const template = document.querySelector('#exampleTemplate');
        const instance = document.importNode(template.content, true);

        // get content of instance – here the section tag:
        // const section = instance.querySelector('section');
        // do something with section ...

        this.appendChild(instance);
	}
}

customElements.define('custom-section', CustomSection);

per tag style

(whithout shadow dom)

Collect styles from custom attributes and bundle them into a stylesheet.

<custom-section customStyle="background:salmon;color:white;"></custom-section>
<custom-section customStyle="background:rose;color:black;"></custom-section>
// function for appending custom style-sheet
const appendStyle = styles => {
    let css = document.createElement('style');
    css.type = 'text/css';

    if (css.styleSheet){
        css.styleSheet.cssText = styles;
    }else{
        css.appendChild(document.createTextNode(styles));
    } 

    document.getElementsByTagName("head")[0].appendChild(css);
}

let styles="";

class CustomSection extends HTMLElement {
    connectedCallback() {
        // generate random ID
        const randID = Math.ceil(Math.random() * 100000);
        const id = `s-${randID}`;
        
        if(this.hasAttribute('customStyle')) {
            styles += `
                #${id}{ 
                    ${this.getAttribute('customStyle')} 
                }`;
        }

        // append template as in preceding example ... 

    }
}

customElements.define('custom-section', CustomSection);

// append styles
appendStyle(styles);

Intersection Observer

lazy-loading example

HTML (note: 1x1px spacer gif as src)

<img class="lazy-img"  src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="lazyDog.jpg">
<img class="img lazy-img" ... >

JS:

const ioOpt = {
    // root: null,
    // threshold: 0.2
}

const ioCallback = (entries, observer) =>{
    entries.forEach( entry => {
        const img = entry.target;

        if( entry.intersectionRatio > 0 ){
            img.src = img.dataset.src;
            observer.unobserve( img );      
        }
        
    } );
} 

const observer = new IntersectionObserver( ioCallback, ioOpt);

const targets = document.querySelectorAll(".lazy-img");
targets.forEach( target => {
    observer.observe(target);
} );

Notes:

  • Options: if root ist omitted or null, the viewport is used as the root element
  • Options: threshold 0.0 – 1.0 how much (percentage) of the target hast to be intersecting with the root to fire the callback
  • To know if the content is above or below, compare if entry.boundingClientRect.y is greater/lesser than entry.rootBounds.y.

JS clipboard

Random js snippets – causing headaches or just stumbled upon while looking at the sun.

Vars & tenary

Setting a bool variable
const isAvailable = item.status === 'available' //bool

Tenary operator:
condition ? trueVal : fasleVal

Examples

  • const volljaehrig = (age >= 18) ? "Ja" : Nein";
  • return item ? item.name : 'the item'; (if item is defined, return the item name, otherwse return ‚the item‘ instead


document.ready equivalent

document.addEventListener("DOMContentLoaded", function() {
  // code...
});

selecting etc.

element.matches(selector) // returns bool

element.closest(selectors);

// example:
if( menuItem.matches(.active) ){
  // menuItem has class 'active'
  const nextMenuItem = menuItem.closest("li");
}

see: Element.matches(), Element.closest() at MDN

map, filter, reduce

map()

💡 [a,b,c][1,2,3]

Use it when: You want to translate/map all elements in an array to another set of values. [1]

// multiply all elements by 10
const numbers = [1,2,3]
const timesTen = array.map(number => number*10); // [10,20,30]

more details + all params see: Dan Martensen or MDN

filter()

💡 [a,b,c][a,c]

Use it when: You want to remove unwanted elements based on a condition. [1]

const data = ["a","","c"];
const notEmpty = data.filter(elem => "" !== elem); // ["a","c"]

more details + all params see: Dan Martensen or MDN

reduce()

💡 [1,2,3]6

Use it when: You want to find a cumulative [+] or concatenated value based on elements across the array. [1]

Arguments: (in Order) added value of last itterations, value of current itteration, (opt) index of itteration, (opt) original array invoking the method (??) and at the end the starting number

const numbers = [1,2,3]
const sum = numbers.reduce( (prevVal, currentVal) => prevVal + currentVal, 0 ); // 6

note that the prevVal is already the computed value of the previous iterations. If getting values from an object, e.g. person.age only the currentVal needs to access that property ( ... => prevVal + currentVal.age, 0 ). The first is already the sum of all previous ages.

sort()

💡 [3,1,2] → [1,2,3]

compares two items of the array at a time. If the comparing function returns 1, the current item is stacked on top of / listed before the second, if  -1 is returned, the current item is stacked underneath / listed after.

const nums = [3,1,2,4];
const numsSorted = nums.sort( (a,b) => a > b ? 1 : -1 ); 
// [1,2,3,4,5]

note  all of the methods above are chainable, eg:

const linksContainingOf = links
    .map(el => el.textContent)
    .filter(txt => txt.includes('of'));

more details + all params see: Dan Martensen or MDN

[1] Dan Martensen – JavaScript’s Map, Reduce, and Filter 

Getting the scroll position

  • scroll event: scroll 
  • scroll offset of an element: element.scrollTop
  • global scroll position: document.documentElement.scrollTop (sic. document.body.scrollTop is deprecated).
    • or (browser support madness):
      window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0 (soruce)

Optional Chaining

propertyKey?.

const users = [
  {
    name: "sxxm",
    roles: {
      canEdit: true,
      canDelete: false
   },
  },
   {
    name: "g33k",
    // has no roles {}
   }
];

for (const user of users) {
  console.log(user?.roles?.canEdit)
}
// Output:
// true
// undefined

Also checkout passive event listeners: https://stackoverflow.com/questions/37721782/what-are-passive-event-listeners#37721906

ES 6 cheat sheet

default variables

calculateBill(total, tax = 0.13, tip = 0.15) {
 return total + (total * tax) + (total * tip);
}

calculatBill(100);
// or
calculateBill(100, undefined, 0.25);

template litterals

(using backticks and ${varname})

// concatenate string
`Hello ${variable}`;

// do js within {}
`The result is ${number * 7}`

// functions within {}
`The result is ${calculateResult()}`

// Markup, whitespace / new lines are allowed within backticks
`<div>
    <h1>${title}</h1>
    <p>${content}</p>
</div>` 

// markup population using prototype.map()
`<ul>
   ${items.map(item => `<li>${item.name} – ${item.price}</li>` )}
</ul>`

arrow functions

note: other than with regular fuctions this refers to the parent

// implicit return
const timesTen = number => number*10; 

// is the same as ...
var timesTen = function(number){
    return number*10;
}

timesTen(4); // 40

string functions

const str = 'Hello World';

str.startsWith('Hello') // true
str.endsWith('rld') // true
str.includes('ello') // true

note: All the functions above are case sensitive. startsWith and endsWith takes an optional second parameter for an offset

const str1 = 'You can stand under my umber';
const str2 = 'ella, ';
const str3 = 'eh, ';

console.log (str1 + str2.repeat(3) + str3.repeat(3)); // You can stand under my umberella, ella, ella, eh, eh, eh, 

destructuring

objects

// the object
const person = {
  'fname' : 'Jon',
  'lname' : 'Doe',
  'age' : 31,
  'skills' : {
    'programming' : {
        'html' : 'good',
        'js'   : 'mediocre'
    },
    'social' : {
        'communication' : 'bad'
    }
  }
}
// top level variables
const {fname, lname, age} = person;

// top level vars from nested objects
// renaming (js => javascrpt)
// assigning default value (css = 'avarage')
const {html, js:javascript, css = 'average'} = person.skills.programming;

console.log(fname); // 'Jon'
console.log(html); // 'good';
console.log(javascript); // 'mediocre';
console.log(css); // 'average'

arrays

same as above, but uses square brackets notation.
note: as an extra the example below shows off the split function to generate an array from a string

// data as string
const data = "Billie Holiday,Strange Fruit,1939,78 rpm";

// assign variables on data splited into an array
const [artist,title,released,format] = data.split(',');

console.log(`${title} by ${artist}, released in ${released} on ${format}`);
// "Strange Fruit by Billie Holiday, released in 1939 on 78 rpm"

note: it’s also possible to use the rest opperator …

const [artitst,title, ...details]

console.log(details) // ["1939","78 rpm"]

swap variables (w/ destructuring)

let current = "project-b";
let previous = "project-a";

[current,previous] = [previous,current]; // currrent = "project-a" and vice versa

using destructuring for function params

instead of a list of params use an object (wrap params whithin {}).
that way they are going to be destructured which means you can call the function with params in any order and skip any param (as long as theres a default value defined)

function calculatePrice( { price, shipping = 15, tax = 0.08 } ){
  return (price + shipping) * ( 1 + tax );
}

const price = calculatePrice({ shipping: 20, price: 200 });
// price = 237.6;

💡💩 caveat: if the function is called without any params (even if all the params have a default value assigned) an error will be thrown because an object is expected. To fix this, the object would need a default value itself:

function calculatePrice( { price = 200, shipping = 15, ... } = {} ) {
  // ...
}

for of loop

for (const item in items){
   // do sth with item
}

pros: lean syntax (not as with for loop), able to use break / continiue etc. (not as with forEach loop), ignores added properties (e.g. to prototype) which would be shown in a for in loop. Works on all itterables
cons: does not work with object(s). Something like Object.entries() (ES 2017) or Object.keys(object) (converts keys of objects in an array) would be necessary

With params destructuring and generators ( → .entries() ), the index can be easily retrieved as well:

// destructing + generator
for ( const [i, item] of items.entries() ){
  console.log( `${item} at index ${i}` ); 
}

// arguments keyowrd (returning all passed in params)
function logOutArgs(){
  for ( arg of arguments ){
    console.log( `Argument ${arg} received.`);
  }
}
logOutArgs("Hello",2,"booyah!");

Array methods

Array.from()
turns array-like collections (NodeLists etc) into a propper array.
Takes a map function as optional second arg.

const el = document.querySelector(".list-element"); // NodeList
const elArr = Array.from(el); // Array

// with map function
const elArr2 = Array.from(el, el => { /* do some mapping, eg. return el.textContent */ })

Array.of() builds an array from the arguments

const fibonacci = Array.of(1,1,2,3,5,8,13); // [1,1,2,3,5,8,13]

.find( callback )
loops over the array, returns the value of the first element in the array that satisfies the provided testing function (MDN)

const data = [
  {
    "id":"Aq415",
    "content":"Hello World"
  },
  {
    "id":"234nA",
    "content":"Some other text"
  },
  {
   ...
  }
];

const entry = data.find( entry => entry.id === "234nA" ); // returns only the object that matches the criteria

.findIndex( callback ) similar to .find() but returns the index of the first element instead of the value.

.some( callback ) retuns true if at least one of the array elements satisfies the provided testing function

.every( callback ) retruns true if every array elements meets the condtition.

Spread operator (…)

expands all items of an itterable.
Can be used to copy or concatenate arrays:

const arr1 = [1,2,3];

// copy
const arr1Copy = [...arr]; // [1,2,3] like arr1 but independant from it

// concatenate
const arr2 = [4,5,6];
const arrBoth = [...arr1,...arr2]; // [1,2,3,4,5,6]
// where [arr1,arr2] would result in [[1,2,3],[4,5,6]]

// concatenate and expand
const arr0to7 = [0,...arr1,...arr2,7] // [0,1,2,3,4,5,6,7]

Rest operator (…)

bundles reaming arguments in an array. Use for function definition or destructuring (see destructring > arrays above)

function createItem( name, price, ...properties ){
  return `${name}: ${price}USD. ${properties.join()}`;
}

const newItem = createItem('Shirt', 9.95, 'machine washable', 'limited edition', 'signed'); // "Shirt: 9.95USD. machine washable,limited edition, signed";

Object litterals updates

// when creating an object from variables with the same name it suffices to just write that var name

const fname = "Jon";
const lname = "Doe";
const person = { fname, lname } // instead of { fname: fname, lname: lname }

// while creating object methods, the function keywortd and the colon can be omitted


const createItem = {
  // old
  calculatePrice: function(){
    ...
  }
  // new
  calculatePrice() {
    ...
  }
}

fetch

Fetches resources. «A modern replacement for XMLHttpRequest» (caniuse)
Returns a promise.
Implementet in all modern browsers (which obviously means no IE)

// the fetch
const postPromise = fetch("https://code.lrnz.ch/wp-json/wp/v2/posts");

// working with the promised data
postPromise
    .then( data => data.json() ) // we want json
    .then( data => { /* do something with post data */ } )
    .catch( error => { /* fetch error */ } )

(for a fetch() + JSON deep dive see here)

Promises

// promise constructor returns takes a function with two params: resolve if promise is resolved and reject if an error is thrown.

const promise = new Promise( (resolve, reject) => {
  const data = /* some data eg from DB query */;
  if( data ){
    resolve( data ); 
  }else{
    reject( Error('error message') );
  }
});

promise
  .then( (data) => { /* do something with the resolved or rejected data */ } )

Example: Get data of a post from its id using the WP Rest API and fetch().

function fetchPost(url, id) {

    const postData = fetch(url);
    return postData
        .then(data => data.json())
        .then(data => {
            const post = data.find(entry => entry.id === id);
            if (post) {
                return post;
           }
        })
        .catch(err => {
            console.error('Error fetching api data');
        });

}

// fetch data of this post (id:965)        
fetchPost('https://code.lrnz.ch/wp-json/wp/v2/posts', 965)
    .then(post => { console.log(post); })
    .catch(err => {
        console.error('Error fetching post');
    });

Stepping/chaining

If a promise is returned within a .then, it can be retreived with another (chained) .then

function getData( data => {
  return new Promise((resolve, reject) => {
     const d = /* do sth with data */;
     if( d ) {
       resolve( d );
     }
  });
});

function enrichData( data => {
  return new Promise((resolve, reject) => {
     if( data.details ) {
       resolve( data.details );
     }
  });
})

getData( someData )
  .then( data => {
    // first dataset arrived
    // call other function with the returned data
    enrichData(data)
  })
  .then( data => {
     // second, 'enriched' dataset arrived 
  })
  .catch( err => { /* ... */ } )

Promise.all()

Waits for all given promises (array as param) to be resolved until firing .then()

const promise1 = new Promise( ... );
const promise2 = new Promise( ... );

Promise
  .all([promise1, promise2])
  .then( ... )

Glossar

itterable : everything that can be looped over (array, dom collection, map, set … ). 💡💩 objects are not itterable!

To checkout …

Things seen on the way or reminders of functions/methods and properties. Maybe not all ES6 but worth checking out.

  • el.textContent : property, represents the text content of a node and its descendants. see: Node.textContent
  • arr1.concat(arr2) : merges two or more array. see: Array.prototype.concat()
    • alternatively use spead operator (…) to achive the same:
      [...arr1, ...arr2]

Credits

ES6 allmost all examples above were written down while following the ES6 for everyone course by Wes Bos.
Additional notes coming from the MDN Web docs.

js statt JQ

Masonry / Isotope, ImagesLoaded, InfiniteScroll

(the evil trinity)

Files & Docs: Masonry, Isotope, ImagesLoaded, InfiniteScroll

Masonry/Isotope with InfiniteScroll:

Random string/char sequence

JS

Math.random().toString(36).substring(7);

source: stackoverflow (1)
advanced & explained example: stackoverflow (2)

 

PHP

function generateRandomString($length = 10) {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;
}

source: stackoverflow (3)
generating secure(r) tokens, see stackoverflow (4)

 

jQuery REST GET ajax

Accessing REST API json with jQuery:

$.ajax({
    url: "https://url.com/wp-json/wp/v2/posts"
}).then(function(data) {
	for (var i = 0; i < data.length; i++) {
		$('.container').append(data[i].slug + "<br>");
	}		
});

See: Consuming a RESTful Web Service with jQuery, spring.io  (code slightly adapted for WP REST)

nodes examples: idlinktitle.renderedcontent.renderedexcerpt.rendered

 

 

Javascript refresher

Objects & Classes

function Animal(type, numLegs){
    this.type = type;
    this.numLegs = numLegs; 
}

function Dog(height){
    this.height = height;  //public property  
    var avgHeightCM = 50;  //private property
    this.isAvgHeight = function(){  // method
        if( this.height > avgHeightCM ){
            return "Above average height";
        }else{
            return "Below or equal avarage height";
        }
    }  
}

Dog.prototype = new Animal(); // inheritance

var fido = new Dog(55); 
fido.isAvgHeight(); // returns "Above average height"
fido.numLegs = 4;

Accessing Objects

Sidenote: There are no string/litteral idexes vor JS Arrays – only int indexes! Use objects instead.

const apples = {
  grannySmith: { color: 'green', ... },
  gala: { color: 'yellow' } 
}

// Access Color of Gala
const galaColor = apples.gala.color

// same as ...
const galaColor = apples['gala']['color']

// the latter is useful in conjunction with variables
const desiredApple = 'gala';
const desiredAttr  = 'color';
const galaColor = apples[desiredApple][desiredAttr];

Lintinng

CSS

  • Doiuse – checks CSS file for Browsersupport against caniuse.com (command line / js)

 

JS

  • JSCC – online javascript compatibility checker (copy&paste)

JS effect libs