## Browser

#### arrayToHtmlList

Converts the given array elements into `<li>` tags and appends them to the list of the given id.

Use `Array.map()`, `document.querySelector()`, and an anonymous inner closure to create a list of html tags.

    const arrayToHtmlList = (arr, listID) =>
      (el => (
        (el = document.querySelector('#' + listID)),
        (el.innerHTML += arr.map(item => `<li>${item}</li>`).join(''))
      ))();

examples

    arrayToHtmlList(['item 1', 'item 2'], 'myListID');

#### bottomVisible

Returns `true` if the bottom of the page is visible, `false` otherwise.

Use `scrollY`, `scrollHeight` and `clientHeight` to determine if the bottom of the page is visible.

    const bottomVisible = () =>
      document.documentElement.clientHeight + window.scrollY >=
      (document.documentElement.scrollHeight || document.documentElement.clientHeight);

examples

    bottomVisible(); // true

#### copyToClipboard

⚠️ NOTICE: The same functionality can be easily implemented by using the new asynchronous Clipboard API, which is still experimental but should be used in the future instead of this snippet. Find out more about it here.

Copy a string to the clipboard. Only works as a result of user action (i.e. inside a `click` event listener).

Create a new `<textarea>` element, fill it with the supplied data and add it to the HTML document. Use `Selection.getRangeAt()`to store the selected range (if any). Use `document.execCommand('copy')` to copy to the clipboard. Remove the `<textarea>` element from the HTML document. Finally, use `Selection().addRange()` to recover the original selected range (if any).

    const copyToClipboard = str => {
      const el = document.createElement('textarea');
      el.value = str;
      el.setAttribute('readonly', '');
      el.style.position = 'absolute';
      el.style.left = '-9999px';
      document.body.appendChild(el);
      const selected =
        document.getSelection().rangeCount > 0 ? document.getSelection().getRangeAt(0) : false;
      el.select();
      document.execCommand('copy');
      document.body.removeChild(el);
      if (selected) {
        document.getSelection().removeAllRanges();
        document.getSelection().addRange(selected);
      }
    };

examples

    copyToClipboard('Lorem ipsum'); // 'Lorem ipsum' copied to clipboard.

#### counter

Creates a counter with the specified range, step and duration for the specified selector.

Check if `step` has the proper sign and change it accordingly. Use `setInterval()` in combination with `Math.abs()` and `Math.floor()` to calculate the time between each new text draw. Use `document.querySelector().innerHTML` to update the value of the selected element. Omit the fourth parameter, `step`, to use a default step of `1`. Omit the fifth parameter, `duration`, to use a default duration of `2000`ms.

    const counter = (selector, start, end, step = 1, duration = 2000) => {
      let current = start,
        _step = (end - start) * step < 0 ? -step : step,
        timer = setInterval(() => {
          current += _step;
          document.querySelector(selector).innerHTML = current;
          if (current >= end) document.querySelector(selector).innerHTML = end;
          if (current >= end) clearInterval(timer);
        }, Math.abs(Math.floor(duration / (end - start))));
      return timer;
    };

examples

    counter('#my-id', 1, 1000, 5, 2000); // Creates a 2-second timer for the element with id="my-id"

#### createElement

Creates an element from a string (without appending it to the document). If the given string contains multiple elements, only the first one will be returned.

Use `document.createElement()` to create a new element. Set its `innerHTML` to the string supplied as the argument. Use `ParentNode.firstElementChild` to return the element version of the string.

    const createElement = str => {
      const el = document.createElement('div');
      el.innerHTML = str;
      return el.firstElementChild;
    };

examples

    const el = createElement(
      `<div class="container">
        <p>Hello!</p>
      </div>`
    );
    console.log(el.className); // 'container'

#### createEventHub

Creates a pub/sub (publish–subscribe) event hub with `emit`, `on`, and `off` methods.

Use `Object.create(null)` to create an empty `hub` object that does not inherit properties from `Object.prototype`. For `emit`, resolve the array of handlers based on the `event` argument and then run each one with `Array.forEach()` by passing in the data as an argument. For `on`, create an array for the event if it does not yet exist, then use `Array.push()` to add the handler to the array. For `off`, use `Array.findIndex()` to find the index of the handler in the event array and remove it using `Array.splice()`.

    const createEventHub = () => ({
      hub: Object.create(null),
      emit(event, data) {
        (this.hub[event] || []).forEach(handler => handler(data));
      },
      on(event, handler) {
        if (!this.hub[event]) this.hub[event] = [];
        this.hub[event].push(handler);
      },
      off(event, handler) {
        const i = (this.hub[event] || []).findIndex(h => h === handler);
        if (i > -1) this.hub[event].splice(i, 1);
      }
    });

examples

    const handler = data => console.log(data);
    const hub = createEventHub();
    let increment = 0;

    // Subscribe: listen for different types of events
    hub.on('message', handler);
    hub.on('message', () => console.log('Message event fired'));
    hub.on('increment', () => increment++);

    // Publish: emit events to invoke all handlers subscribed to them, passing the data to them as an argument
    hub.emit('message', 'hello world'); // logs 'hello world' and 'Message event fired'
    hub.emit('message', { hello: 'world' }); // logs the object and 'Message event fired'
    hub.emit('increment'); // `increment` variable is now 1

    // Unsubscribe: stop a specific handler from listening to the 'message' event
    hub.off('message', handler);

#### currentURL

Returns the current URL.

Use `window.location.href` to get current URL.

    const currentURL = () => window.location.href;

examples

    currentURL(); // 'https://google.com'

#### detectDeviceType

Detects wether the website is being opened in a mobile device or a desktop/laptop.

Use a regular expression to test the `navigator.userAgent` property to figure out if the device is a mobile device or a desktop/laptop.

    const detectDeviceType = () =>
      /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
        ? 'Mobile'
        : 'Desktop';

examples

    detectDeviceType(); // "Mobile" or "Desktop"

#### elementContains

Returns `true` if the `parent` element contains the `child` element, `false` otherwise.

Check that `parent` is not the same element as `child`, use `parent.contains(child)` to check if the `parent` element contains the `child` element.

    const elementContains = (parent, child) => parent !== child && parent.contains(child);

examples

    elementContains(document.querySelector('head'), document.querySelector('title')); // true
    elementContains(document.querySelector('body'), document.querySelector('body')); // false

#### elementIsVisibleInViewport

Returns `true` if the element specified is visible in the viewport, `false` otherwise.

Use `Element.getBoundingClientRect()` and the `window.inner(Width|Height)` values to determine if a given element is visible in the viewport. Omit the second argument to determine if the element is entirely visible, or specify `true` to determine if it is partially visible.

    const elementIsVisibleInViewport = (el, partiallyVisible = false) => {
      const { top, left, bottom, right } = el.getBoundingClientRect();
      const { innerHeight, innerWidth } = window;
      return partiallyVisible
        ? ((top > 0 && top < innerHeight) || (bottom > 0 && bottom < innerHeight)) &&
            ((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth))
        : top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth;
    };

examples

    // e.g. 100x100 viewport and a 10x10px element at position {top: -1, left: 0, bottom: 9, right: 10}
    elementIsVisibleInViewport(el); // false - (not fully visible)
    elementIsVisibleInViewport(el, true); // true - (partially visible)

#### getScrollPosition

Returns the scroll position of the current page.

Use `pageXOffset` and `pageYOffset` if they are defined, otherwise `scrollLeft` and `scrollTop`. You can omit `el` to use a default value of `window`.

    const getScrollPosition = (el = window) => ({
      x: el.pageXOffset !== undefined ? el.pageXOffset : el.scrollLeft,
      y: el.pageYOffset !== undefined ? el.pageYOffset : el.scrollTop
    });

examples

    getScrollPosition(); // {x: 0, y: 200}

#### getStyle

Returns the value of a CSS rule for the specified element.

Use `Window.getComputedStyle()` to get the value of the CSS rule for the specified element.

    const getStyle = (el, ruleName) => getComputedStyle(el)[ruleName];

examples

    getStyle(document.querySelector('p'), 'font-size'); // '16px'

#### hasClass

Returns `true` if the element has the specified class, `false` otherwise.

Use `element.classList.contains()` to check if the element has the specified class.

    const hasClass = (el, className) => el.classList.contains(className);

examples

    hasClass(document.querySelector('p.special'), 'special'); // true

#### hashBrowser

Creates a hash for a value using the SHA-256 algorithm. Returns a promise.

Use the SubtleCrypto API to create a hash for the given value.

    const hashBrowser = val =>
      crypto.subtle.digest('SHA-256', new TextEncoder('utf-8').encode(val)).then(h => {
        let hexes = [],
          view = new DataView(h);
        for (let i = 0; i < view.byteLength; i += 4)
          hexes.push(('00000000' + view.getUint32(i).toString(16)).slice(-8));
        return hexes.join('');
      });

examples

    hashBrowser(JSON.stringify({ a: 'a', b: [1, 2, 3, 4], foo: { c: 'bar' } })).then(console.log); // '04aa106279f5977f59f9067fa9712afc4aedc6f5862a8defc34552d8c7206393'

#### hide

Hides all the elements specified.

Use `NodeList.prototype.forEach()` to apply `display: none` to each element specified.

    const hide = els => els.forEach(e => (e.style.display = 'none'));

examples

    hide(document.querySelectorAll('img')); // Hides all <img> elements on the page

#### httpsRedirect

Redirects the page to HTTPS if its currently in HTTP. Also, pressing the back button doesn't take it back to the HTTP page as its replaced in the history.

Use `location.protocol` to get the protocol currently being used. If it's not HTTPS, use `location.replace()` to replace the existing page with the HTTPS version of the page. Use `location.href` to get the full address, split it with `String.split()` and remove the protocol part of the URL.

    const httpsRedirect = () => {
      if (location.protocol !== 'https:') location.replace('https://' + location.href.split('//')[1]);
    };

examples

    httpsRedirect(); // If you are on http://mydomain.com, you are redirected to https://mydomain.com

#### insertAfter

Inserts an HTML string after the end of the specified element.

Use `el.insertAdjacentHTML()` with a position of `'afterend'` to parse `htmlString` and insert it after the end of `el`.

    const insertAfter = (el, htmlString) => el.insertAdjacentHTML('afterend', htmlString);

examples

    insertAfter(document.getElementById('myId'), '<p>after</p>'); // <div id="myId">...</div> <p>after</p>

#### insertBefore

Inserts an HTML string before the start of the specified element.

Use `el.insertAdjacentHTML()` with a position of `'beforebegin'` to parse `htmlString` and insert it before the start of `el`.

    const insertBefore = (el, htmlString) => el.insertAdjacentHTML('beforebegin', htmlString);

examples

    insertBefore(document.getElementById('myId'), '<p>before</p>'); // <p>before</p> <div id="myId">...</div>

#### isBrowserTabFocused

Returns `true` if the browser tab of the page is focused, `false` otherwise.

Use the `Document.hidden` property, introduced by the Page Visibility API to check if the browser tab of the page is visible or hidden.

    const isBrowserTabFocused = () => !document.hidden;

examples

    isBrowserTabFocused(); // true

#### nodeListToArray

Converts a `NodeList` to an array.

Use spread operator inside new array to convert a `NodeList` to an array.

    const nodeListToArray = nodeList => [...nodeList];

examples

    nodeListToArray(document.childNodes); // [ <!DOCTYPE html>, html ]

#### observeMutations

Returns a new MutationObserver and runs the provided callback for each mutation on the specified element.

Use a `MutationObserver` to observe mutations on the given element. Use `Array.forEach()` to run the callback for each mutation that is observed. Omit the third argument, `options`, to use the default options (all `true`).

    const observeMutations = (element, callback, options) => {
      const observer = new MutationObserver(mutations => mutations.forEach(m => callback(m)));
      observer.observe(
        element,
        Object.assign(
          {
            childList: true,
            attributes: true,
            attributeOldValue: true,
            characterData: true,
            characterDataOldValue: true,
            subtree: true
          },
          options
        )
      );
      return observer;
    };

examples

    const obs = observeMutations(document, console.log); // Logs all mutations that happen on the page
    obs.disconnect(); // Disconnects the observer and stops logging mutations on the page

#### off

Removes an event listener from an element.

Use `EventTarget.removeEventListener()` to remove an event listener from an element. Omit the fourth argument `opts` to use `false` or specify it based on the options used when the event listener was added.

    const off = (el, evt, fn, opts = false) => el.removeEventListener(evt, fn, opts);

examples

    const fn = () => console.log('!');
    document.body.addEventListener('click', fn);
    off(document.body, 'click', fn); // no longer logs '!' upon clicking on the page

#### on

Adds an event listener to an element with the ability to use event delegation.

Use `EventTarget.addEventListener()` to add an event listener to an element. If there is a `target` property supplied to the options object, ensure the event target matches the target specified and then invoke the callback by supplying the correct `this` context. Returns a reference to the custom delegator function, in order to be possible to use with `off`. Omit `opts` to default to non-delegation behavior and event bubbling.

    const on = (el, evt, fn, opts = {}) => {
      const delegatorFn = e => e.target.matches(opts.target) && fn.call(e.target, e);
      el.addEventListener(evt, opts.target ? delegatorFn : fn, opts.options || false);
      if (opts.target) return delegatorFn;
    };

examples

    const fn = () => console.log('!');
    on(document.body, 'click', fn); // logs '!' upon clicking the body
    on(document.body, 'click', fn, { target: 'p' }); // logs '!' upon clicking a `p` element child of the body
    on(document.body, 'click', fn, { options: true }); // use capturing instead of bubbling

#### onUserInputChange

Run the callback whenever the user input type changes (`mouse` or `touch`). Useful for enabling/disabling code depending on the input device. This process is dynamic and works with hybrid devices (e.g. touchscreen laptops).

Use two event listeners. Assume `mouse` input initially and bind a `touchstart` event listener to the document. On `touchstart`, add a `mousemove` event listener to listen for two consecutive `mousemove` events firing within 20ms, using `performance.now()`. Run the callback with the input type as an argument in either of these situations.

    const onUserInputChange = callback => {
      let type = 'mouse',
        lastTime = 0;
      const mousemoveHandler = () => {
        const now = performance.now();
        if (now - lastTime < 20)
          (type = 'mouse'), callback(type), document.removeEventListener('mousemove', mousemoveHandler);
        lastTime = now;
      };
      document.addEventListener('touchstart', () => {
        if (type === 'touch') return;
        (type = 'touch'), callback(type), document.addEventListener('mousemove', mousemoveHandler);
      });
    };

examples

    onUserInputChange(type => {
      console.log('The user is now using', type, 'as an input method.');
    });

#### prefix

Returns the prefixed version (if necessary) of a CSS property that the browser supports.

Use `Array.findIndex()` on an array of vendor prefix strings to test if `document.body` has one of them defined in its `CSSStyleDeclaration` object, otherwise return `null`. Use `String.charAt()` and `String.toUpperCase()` to capitalize the property, which will be appended to the vendor prefix string.

    const prefix = prop => {
      const capitalizedProp = prop.charAt(0).toUpperCase() + prop.slice(1);
      const prefixes = ['', 'webkit', 'moz', 'ms', 'o'];
      const i = prefixes.findIndex(
        prefix => typeof document.body.style[prefix ? prefix + capitalizedProp : prop] !== 'undefined'
      );
      return i !== -1 ? (i === 0 ? prop : prefixes[i] + capitalizedProp) : null;
    };

examples

    prefix('appearance'); // 'appearance' on a supported browser, otherwise 'webkitAppearance', 'mozAppearance', 'msAppearance' or 'oAppearance'

#### recordAnimationFrames

Invokes the provided callback on each animation frame.

Use recursion. Provided that `running` is `true`, continue invoking `window.requestAnimationFrame()` which invokes the provided callback. Return an object with two methods `start` and `stop` to allow manual control of the recording. Omit the second argument, `autoStart`, to implicitly call `start` when the function is invoked.

    const recordAnimationFrames = (callback, autoStart = true) => {
      let running = true,
        raf;
      const stop = () => {
        running = false;
        cancelAnimationFrame(raf);
      };
      const start = () => {
        running = true;
        run();
      };
      const run = () => {
        raf = requestAnimationFrame(() => {
          callback();
          if (running) run();
        });
      };
      if (autoStart) start();
      return { start, stop };
    };

examples

    const cb = () => console.log('Animation frame fired');
    const recorder = recordAnimationFrames(cb); // logs 'Animation frame fired' on each animation frame
    recorder.stop(); // stops logging
    recorder.start(); // starts again
    const recorder2 = recordAnimationFrames(cb, false); // `start` needs to be explicitly called to begin recording frames

#### redirect

Redirects to a specified URL.

Use `window.location.href` or `window.location.replace()` to redirect to `url`. Pass a second argument to simulate a link click (`true` - default) or an HTTP redirect (`false`).

    const redirect = (url, asLink = true) =>
      asLink ? (window.location.href = url) : window.location.replace(url);

examples

    redirect('https://google.com');

#### runAsync

Runs a function in a separate thread by using a Web Worker, allowing long running functions to not block the UI.

Create a new `Worker` using a `Blob` object URL, the contents of which should be the stringified version of the supplied function. Immediately post the return value of calling the function back. Return a promise, listening for `onmessage` and `onerror` events and resolving the data posted back from the worker, or throwing an error.

    const runAsync = fn => {
      const worker = new Worker(
        URL.createObjectURL(new Blob([`postMessage((${fn})());`]), {
          type: 'application/javascript; charset=utf-8'
        })
      );
      return new Promise((res, rej) => {
        worker.onmessage = ({ data }) => {
          res(data), worker.terminate();
        };
        worker.onerror = err => {
          rej(err), worker.terminate();
        };
      });
    };

examples

    const longRunningFunction = () => {
      let result = 0;
      for (let i = 0; i < 1000; i++) {
        for (let j = 0; j < 700; j++) {
          for (let k = 0; k < 300; k++) {
            result = result + i + j + k;
          }
        }
      }
      return result;
    };
    /*
      NOTE: Since the function is running in a different context, closures are not supported.
      The function supplied to `runAsync` gets stringified, so everything becomes literal.
      All variables and functions must be defined inside.
    */
    runAsync(longRunningFunction).then(console.log); // 209685000000
    runAsync(() => 10 ** 3).then(console.log); // 1000
    let outsideVariable = 50;
    runAsync(() => typeof outsideVariable).then(console.log); // 'undefined'

#### scrollToTop

Smooth-scrolls to the top of the page.

Get distance from top using `document.documentElement.scrollTop` or `document.body.scrollTop`. Scroll by a fraction of the distance from the top. Use `window.requestAnimationFrame()` to animate the scrolling.

    const scrollToTop = () => {
      const c = document.documentElement.scrollTop || document.body.scrollTop;
      if (c > 0) {
        window.requestAnimationFrame(scrollToTop);
        window.scrollTo(0, c - c / 8);
      }
    };

examples

    scrollToTop();

#### setStyle

Sets the value of a CSS rule for the specified element.

Use `element.style` to set the value of the CSS rule for the specified element to `val`.

    const setStyle = (el, ruleName, val) => (el.style[ruleName] = val);

examples

    setStyle(document.querySelector('p'), 'font-size', '20px'); // The first <p> element on the page will have a font-size of 20px

#### show

Shows all the elements specified.

Use the spread operator (`...`) and `Array.forEach()` to clear the `display` property for each element specified.

    const show = (...el) => [...el].forEach(e => (e.style.display = ''));

examples

    show(...document.querySelectorAll('img')); // Shows all <img> elements on the page

#### smoothScroll

Smoothly scrolls the element on which it's called into the visible area of the browser window.

Use `.scrollIntoView` method to scroll the element. Pass `{ behavior: 'smooth' }` to `.scrollIntoView` so it scrolls smoothly.

    const smoothScroll = element =>
      document.querySelector(element).scrollIntoView({
        behavior: 'smooth'
      });

examples

    smoothScroll('#fooBar'); // scrolls smoothly to the element with the id fooBar
    smoothScroll('.fooBar'); // scrolls smoothly to the first element with a class of fooBar

#### toggleClass

Toggle a class for an element.

Use `element.classList.toggle()` to toggle the specified class for the element.

    const toggleClass = (el, className) => el.classList.toggle(className);

examples

    toggleClass(document.querySelector('p.special'), 'special'); // The paragraph will not have the 'special' class anymore

#### triggerEvent

Triggers a specific event on a given element, optionally passing custom data.

Use `new CustomEvent()` to create an event from the specified `eventType` and details. Use `el.dispatchEvent()` to trigger the newly created event on the given element. Omit the third argument, `detail`, if you do not want to pass custom data to the triggered event.

    const triggerEvent = (el, eventType, detail = undefined) =>
      el.dispatchEvent(new CustomEvent(eventType, { detail: detail }));

examples

    triggerEvent(document.getElementById('myId'), 'click');
    triggerEvent(document.getElementById('myId'), 'click', { username: 'bob' });

#### UUIDGeneratorBrowser

Generates a UUID in a browser.

Use `crypto` API to generate a UUID, compliant with RFC4122 version 4.

    const UUIDGeneratorBrowser = () =>
      ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
        (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16)
      );

examples

    UUIDGeneratorBrowser(); // '7982fcfe-5721-4632-bede-6000885be57d'