Zach's Mugspideyclick logo

GitHub

GitLab

Linkedin

Instagram

Youtube

SoundCloud

Email

JavaScript Tips

Cheat Sheet

http://overapi.com/javascript

Event Reference

https://developer.mozilla.org/en-US/docs/Web/Events

Handy JavaScript Functions

https://gitlab.com/snippets/1820469

Fetch API

Found out about the Fetch API today. I've been using XMLHTTPRequests for too long! Docs here.

Here's a quick example:

document.getElementById('fileUploadForm').onsubmit  =  async  (e)  =>  {
    e.preventDefault();
    let response = await fetch(window.origin + '/forms/templateimport/', {
        method:  'POST',
        body:  new  FormData(document.getElementById('fileUploadForm')),
        'Content-Type':  'application/json',
        headers:  {
            'X-CSRFToken':  Cookies.get('csrftoken'),
        },
    });
    let  result  =  await  response.json();
    drawFields(result.fields);
}

Parsing Date Strings

new Date(Date.parse('2020-05-01')).toLocaleDateString()
>>> "4/30/2020"

See the problem? As per this article, it's much better to do something like this:

/*  @param {string} s - an ISO 8001 format date and time string
**                      with all components, e.g. 2015-11-24T19:40:00
**  @returns {Date} - Date instance from parsing the string. May be NaN.
*/
function parseISOLocal(s) {
  var b = s.split(/\D/);
  return new Date(b[0], b[1]-1, b[2], b[3], b[4], b[5]);
}

document.write(parseISOLocal('2015-11-24T19:40:00'));

Or, if you're just working with dates:

function parseDate(s) {
  var b = s.split(/-/);
  return new Date(b[0], b[1]-1, b[2]);
}

Recursive Functions + For Loops

https://stackoverflow.com/questions/6419128/javascript-for-loop-variable-and-recursion

Cache the length of the array so you would have the following:

function recurse(node) {
    for(var i = 0, count = node.children.length; i < count; i++) {
        recurse(node.children[i]);
    }
}

You should always cache especially when you're dealing with HTMLCollections.

Just another note from me: the "count" variable needs to be unique from other "count" variables used on other loops within the same function.

Note: It may be more worthwhile to use the foreach method on an array. It's easier to read and I think could simplify the code.

Sleep Function

https://flaviocopes.com/javascript-sleep/

Create the sleep function:

const sleep = (milliseconds) => {
  return new Promise(resolve => setTimeout(resolve, milliseconds))
}

Use the sleep function:

sleep(500).then(() => {
  //do stuff
})

Or use it in an async function:

const doSomething = async () => {
  await sleep(2000)
  //do stuff
}


doSomething()

Remember that due to how JavaScript works (read more about the event loop), this does not pause the entire program execution like it might happen in other languages, but instead only your function sleeps.

Onclick inside anchor tag

Sometimes you have a large anchor tag containing some extra buttons that you'd like to have fire off a JavaScript function, while not navigating away from the page. Just add return false to the end of your onclick statement!

<a href="#">
  text here
  <i class="material-icons copyButton" onclick="copyToClipboard('thisText');return false;">
</a>

Nested onclick properties

Say you have a div, which has an onclick property (maybe highlights a card or something). That function might reveal buttons inside the div, with their own onclick properties. How do you prevent the parent's onclick from executing when clicking one of the child buttons? You add the following to the beginning of the child's onclick attribute: event.stopPropagation();.

For example:

<div class="card cardSelected" onclick="selectCard(this);">
    <div class="button deleteButton" onclick="event.stopPropagation();deleteObject('{id}');">Delete</div>
    <div class="button cancelButton" onclick="event.stopPropagation();showEditControls(this.parentNode.parentNode);">Cancel</div>
    <div class="button saveButton" onclick="event.stopPropagation();saveObject('{id}');">Save</div>
</div>

Smooth scroll into view

yourdiv.scrollIntoView({behavior: "smooth", block:"end", inline:"nearest"});

querySelector

https://stackoverflow.com/q/26848289/2887850

It's time to start using querySelector and querySelectorAll! They are both fully supported, utilize existing CSS syntax and are shorter to type (yay!).

UPDATE: Looks like it has worse performance, as per this question. Probably better off to keep using getElementById and getElementsByClassName for that extra bit of performance.

UPDATE 2020-07-08: After a long time thinking and looking at querySelector, I've gotta say the benefits of the cleaner code sure come close to making it worth it. It sure seems to be picking up steam. Maybe I'll use it a little more for now, see how bad the performance hit really is...

JS Classes + HTML Properties

HTML data-attributes are pretty limited. They are just strings! And clutter your DOM. Why not create JS classes that inherit from HTML DOM objects, and put your complex data (including objects) there? Recommended to pair this with methods to get the properties, and events to trigger the methods. Something to think about.

https://itnext.io/handling-data-with-web-components-9e7e4a452e6e

For-loop performance

Keep in mind:

premature optimisation is the root of all evil. measure first. optimise later.

https://medium.com/hackernoon/3-javascript-performance-mistakes-you-should-stop-doing-ebf84b9de951

For Loop, average loop time: ~10 microseconds  
For-Of, average loop time: ~110 microseconds  
ForEach, average loop time: ~77 microseconds  
While, average loop time: ~11 microseconds  
Reduce, average loop time: ~113 microseconds

Await User Input (Resolve promise outside of function)

HTML:

<div  class="btn btn-danger"  onclick="outsideResolve(false);">Cancel</div>
<div  class="btn btn-success"  onclick="outsideResolve(true);">Continue</div>

JS:

var outsideResolve;
async  function  awaitUserInput()  {
let  promise  =  new  Promise(function  (resolve)  {
outsideResolve  =  resolve;
});
var  output  =  await  promise;
return  output;
}
let userHitContinue =  await  awaitUserInput();
console.log(userHitContinue);

Assign output from inline function to variable

myResult = ( function() {return 'a';} ) ();

FETCH a PDF and display in page

var loadedPdf; // Used for releasing the loaded PDF
async  function  getPdf(url, target)  {
    if (loadedPdf) { window.URL.revokeObjectURL(loadedPdf) };
    let  response = await  fetch(url,  { method:  'GET' });
    loadedPdf = await  response.blob();
    document.querySelector(target).setAttribute('data', window.URL.createObjectURL(loadedPdf));
};