Skip to content

wking-io/saladbar

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

logo

Welcome to saladbar, a library of functions built for composition that take a functional approach to working with the DOM.

Why the name saladbar? This library is intended to be used by combining smaller functions to build complete programs. That sounds a lot like how a saladbar works. Also, I like food, and I think library names should be fun.

Usage

npm install --save saladbar

Saladbar is bundled to work with EcmaScript version 5.

For older environments you may need to polyfill one or more of the following functions: Object.assign and Array.isArray.

CommonJS Module

var { hasClass } = require('saladbar');

// Given this html
// <div class="default test"></div>

var hasClassTest = hasClass('test');

hasClassTest('.default')
  .map(console.log); // console.log value
  .leftMap(console.error) // can also leftMap to check for errors
//> true

EcmaScript Module

The package.json sets a module-field for build-tools like Rollup or Webpack.

import { hasClass } from 'saladbar';

// Given this html
// <div class="default test"></div>

const hasClassTest = hasClass('test');

hasClassTest('.default')
  .map(console.log); // console.log value
  .leftMap(console.error) // can also leftMap to check for errors
//> true

Global Bundle (CDN)

Saladbar is hosted in full with all of its dependencies at: https://cdn.rawgit.com/wking-io/saladbar/8618f175/packages/saladbar/lib/umd/saladbar.min.js

This script will add saladbar to the global scope.

Overview

Disclaimer: This library revolves around functional programming concepts. I highly recommend Professor Frisby’s Mostly Adequate Guide To Functional Programming as an introduction. It covers everything you need to get up an going with the concepts practiced in this library.

The goal of this library is to provide a set of focused, composable functions that cover common DOM interactions. Before diving into the API for each of the individual functions there are some universal guarantees and features for every function that I want to cover.

All functions are curried by default

A curried function is a function that when called with fewer arguments than expected, returns a new function that takes the remaining arguments. If you are familiar with currying you know the power this gives you when you are composing functions. If you are not familiar with it, here are some examples that show it usefulness.

You can partially apply some functions to make a more focused utility

/** setProp expects three arguments.
 *
 * 1. A property to set
 * 2. The value to set it to
 * 3. The element(s) to set it on
 *
 * Since functions are curried by default you can partially apply the
 * innerHTML property to the function and create a new function that
 * you can pass around anytime you want to write a value to an element.
 **/
const write = setProp('innerHTML');

/** toggleClass expects two arguments.
 *
 * 1. A class to toggle
 * 2. The dom element(s) to toggle it on
 *
 * Since functions are curried by default you can partially apply the
 * class 'open' to the function and create a new function that you
 * can pass around when you need to toggle an element's open state.
 **/
const toggleOpen = toggleClass('open');

You can partially apply functions that take more than one argument so that they can be composed like LEGO blocks

/** This function composes partially applied functions so that all
 * they need is the color to change the theme to and the element(s)
 * to make those transformations on.
 **/
const changeTheme = color =>
  compose(
    write(`Wow, look I am ${color}`),
    addClass(`theme-${color}`),
    setData('theme', color)
  );

/** You can alse partially apply this function with set colors
 * if you want to have more specific use cases.
 **/
const changeThemeToBlue = changeTheme('blue');
const changeThemeToGreen = changeTheme('green');
const changeThemeToOrange = changeTheme('orange');

/** Now when we run the function below the following steps occur
 *
 * 1. Sets the 'data-theme' attribute to blue and returns the '.page-wrapper' element
 * 2. Receives '.page-wrapper' element from setData and adds the class 'theme-blue' and returns the '.page-wrapper' element
 * 3. Receives '.page-wrapper' element from addClass and sets the innerHTML to 'Wow, look I am blue' and returns the '.page-wrapper' element
 **/
changeThemeToBlue('.page-wrapper');

All functions take the target element(s) last

This is a common and powerful technique to make function composition as easy as possible. In function composition each function in the chain takes the return value of the previous function. So, by passing the target element(s) as the last param we can more easily chain together DOM tranformations on said element(s).

// Function that sets a class, attribute, and style on the same element
const addStuff = compose(
  setAttr('aria-hidden', 'false'),
  addClass('show-element'),
  setStyle('display', 'block')
);

/** You can then call the function with any element you want and
 * that element will be passed through each function in the composition.
 **/
addStuff('.element');

Since the functions might be used at any part of a composition chain all functions in the library allow the final argument to be any of the following cases:

  • If passed a CSS Selector the function will automatically fetch that element from the DOM using the dom function.
  • If passed an element or elements gotten by running querySelector or querySelectorAll they will be wrapped in an Either.
  • If passed an Either they will be checked to make sure it contains an element then it will move on.

All functions return an Either Monad

This is the big one. Every function in this library returns an Either. Every function also handles composing these Either wrapped results by default so that you do not have to worry about how each function composes to the next.

Why this approach? There are two main benefits.

No more runtime errors with β€œundefined is not a function” issues. The Either Type is defined by its ability to evaluate actions and capture their results as either a Left(failure) or a Right(success). Then on subsequent actions to that result, functions only run over the success values. This means if there is an error in your runtime the rest of the actions are ignored and that error is capture for you to handle yourself. It will not crash or show during evaluation.

Guaranteed laws that every value adheres to. The Either Type used in this library adheres to the following algebraic data types as outlined in the Fantasy-Land Spec:

  • Functor
  • Monad
  • Applicative
  • Chain

This means that there are mathematical laws that define how these values can be used and combined. Any implementation that follows these laws is guaranteed to work the exact same.

For a detailed overview of what this means I will point you again to Professor Frisby’s Mostly Adequate Guide To Functional Programming specifically chapters 8-12. Also this series that breaks down the Fantasy-Land Spec and defines it in plain language.

If you are not familiar with some of the functional programming techniques seen above check out these awesome resources:

Examples

Play around with the API that solve the problems below.

Documentation

Table Of Contents

Grabbing elements from the DOM
Setter Functions
Getter Functions
Predicate Functions
Event Functions
Utility Functions

Type signatures

Hindley-Milner type signatures are used to document functions. You might encounter some additional syntax that we use to describe JavaScript specific stuff, like functions that take multiple arguments at once.

Types

You'll find that some signatures refer to concrete types, such as Either. This is reference of the types used throughout the documentation:

  • Either - Instances of Either provided by data.either.
  • Pair a b - An array with exactly two elements: [a, b].
  • Error - An object with a key of error and a string value that represents the error received.
  • DOM Element - Since functions in this library accept multiple types of DOM Element representations (see Overview section above) this type represents any of those accepted values.
  • DOM - Global representing the browser Document
  • Selector - Valid CSS Selector as defined in the spec.

Grabbing elements from the DOM

dom

dom :: (Selector, DOM Element) -> Either Error DOM Element

Returns first element that matches the passed in Selector. If no element is found with that Selector the function will return an Error. You can optionally pass another element to be used as the root of the query similiar to how you can runquerySelector method on any element in the DOM.

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper">
 *   <h1 class="pick-me">Title</h1>
 * </div>
 *
 */

const title = dom('.pick-me');

title.map(console.log);
//> <h1 class="pick-me">Title</h1>

domAll

domAll :: (Selector, DOM Element) -> Either Error [ DOM Element ]

Returns an array of all elements that match the passed in Selector. If no elements are found with that Selector the function will return an Error. You can optionally pass another element to be used as the root for the query similar to how you can run querySelectorAll method on any element in the DOM.

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper">
 *   <ul>
 *     <li class="item">Item One</li>
 *     <li class="item">Item Two</li>
 *     <li class="item">Item Three</li>
 *   </ul>
 * </div>
 *
 */

const listItems = domAll('.item');

title.map(console.log);
//> [<li class="item">Item One</li>, <li class="item">Item Two</li>, <li class="item">Item Three</li>]

findParent

findParent :: pred -> DOM Element -> Either Error DOM Element

Returns parent element that matches the predicate. If no parent is found the body element will be returned. If you pass an array of elements to the function each element will be mapped over and replaced with the parent element found by the predicate for that element.

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper">
 *   <h1 class="pick-me">Title</h1>
 *   <ul class="wrapper">
 *     <li class="item pick-me">Item One</li>
 *     <li class="item">Item Two</li>
 *     <li class="item">Item Three</li>
 *   </ul>
 * </div>
 *
 */

// Find parent of single element
const getSingleWrapper = findParent(hasClass('wrapper'));
getSingleWrapper('.pick-me').map(console.log);
//> <div class="wrapper">...</div>

// Find parent of multiple elements
const getAllWrapper = compose(findParent(hasClass('wrapper')), domAll);
getAllWrapper('.pick-me').map(console.log);
//> [<div class="wrapper">...</div>, <ul class="wrapper">...</ul>]

Setter Functions

addClass

addClass :: String -> DOM Element -> Future Error DOM Element

Adds class/classes to passed in element(s). You can pass in either a single class or an array of classes to be added to a single element or an array of elements.

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper">
 *   <h1 class="pick-me">Title</h1>
 *   <p class="pick-me">Paragraph One</p>
 *   <p class="pick-me">Paragraph Two</p>
 * </div>
 *
 */

// Single class to single element
const addClassGreen = addClass('green');
addClassGreen('.pick-me').map(console.log);
//> <h1 class="pick-me green">Title</h1>

// Multiple classes to single element
const addColorClasses = addClass(['green', 'blue']);
addColorClasses('.pick-me').map(console.log);
//> <h1 class="pick-me green blue">Title</h1>

// Single class to multiple elements
const addClassGreenAll = compose(addClass('green'), domAll);
addClassGreenAll('.pick-me').map(console.log);
//> [<h1 class="pick-me green">Title</h1>, <p class="pick-me green">Paragraph One</p>, <p class="pick-me green">Paragraph Two</p>]

// Multiple classes to multiple elements
const addColorClassesAll = compose(addClass(['green', 'blue']), domAll);
addColorClassesAll('.pick-me').map(console.log);
//> [<h1 class="pick-me green blue">Title</h1>, <p class="pick-me green blue">Paragraph One</p>, <p class="pick-me green blue">Paragraph Two</p>]

removeClass

`removeClass :: String -> DOM Element -> Future Error DOM Element```

Removes class/classes from passed in element(s). You can pass in either a single class or an array of classes to be removed from a single element or an array of elements.

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper">
 *   <h1 class="pick-me green blue">Title</h1>
 *   <p class="pick-me green blue">Paragraph One</p>
 *   <p class="pick-me green blue">Paragraph Two</p>
 * </div>
 *
 */

// Single class on single element
const removeClassGreen = removeClass('green');
removeClassGreen('.pick-me').map(console.log);
//> <h1 class="pick-me blue">Title</h1>

// Multiple classes on single element
const removeColorClasses = removeClass(['green', 'blue']);
removeColorClasses('.pick-me').map(console.log);
//> <h1 class="pick-me">Title</h1>

// Single class on multiple elements
const removeClassGreenAll = compose(removeClass('green'), domAll);
removeClassGreenAll('.pick-me').map(console.log);
//> [<h1 class="pick-me blue">Title</h1>, <p class="pick-me blue">Paragraph One</p>, <p class="pick-me blue">Paragraph Two</p>]

// Multiple classes on multiple elements
const removeColorClassesAll = compose(removeClass(['green', 'blue']), domAll);
removeColorClassesAll('.pick-me').map(console.log);
//> [<h1 class="pick-me">Title</h1>, <p class="pick-me">Paragraph One</p>, <p class="pick-me">Paragraph Two</p>]

replaceClass

replaceClass :: String -> String -> DOM Element -> Either Error DOM Element

Replaces one class with another on passed in element(s). You replace a class on a single element or an array of elements.

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper">
 *   <h1 class="pick-me green">Title</h1>
 *   <p class="pick-me green">Paragraph One</p>
 *   <p class="pick-me green">Paragraph Two</p>
 * </div>
 *
 */

// Replace class on single element
const replaceClassGreen = replaceClass('green', 'blue');
replaceClassGreen('.pick-me').map(console.log);
//> <h1 class="pick-me blue">Title</h1>

// Replace class on multiple elements
const replaceClassGreenAll = compose(replaceClass('green', 'blue'), domAll);
replaceClassGreenAll('.pick-me').map(console.log);
//> [<h1 class="pick-me blue">Title</h1>, <p class="pick-me blue">Paragraph One</p>, <p class="pick-me blue">Paragraph Two</p>]

setAttr

setAttr :: String -> String -> DOM Element -> Either Error DOM Element

Sets the value of the specified attribute on the passed in element(s).

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper pick-me" aria-expanded="false">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 * <div class="wrapper pick-me" aria-expanded="false">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 *
 */

// Update aria-expanded on single element
const expandFirst = setAttr('aria-expanded', 'true');
expandFirst('.pick-me').map(console.log);
//> <div class="wrapper pick-me" aria-expanded="true">...</div>

// Update aria-expanded on multiple elements
const expandAll = compose(setAttr('aria-expanded', 'true'), domAll);
expandAll('.pick-me').map(console.log);
//> [<div class="wrapper pick-me" aria-expanded="true">...</div>, <div class="wrapper pick-me" aria-expanded="true">...</div>]

setData

setData :: String -> String -> DOM Element -> Either Error DOM Element

Sets the value of the specified data-attribute on the passed in element(s). Sets values using the dataset property on DOM Elements so the data attribute you are setting will follow the naming rules outlined here: HTMLElement.dataset

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper pick-me" data-example="before">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 * <div class="wrapper pick-me" data-expample="before">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 *
 */

// Update data-example on single element
const changeFirst = setData('example', 'after');
changeFirst('.pick-me').map(console.log);
//> <div class="wrapper pick-me" data-example="after">...</div>

// Update data-example on multiple elements
const changeAll = compose(setData('example', 'after'), domAll);
changeAll('.pick-me').map(console.log);
//> [<div class="wrapper pick-me" example="after">...</div>, <div class="wrapper pick-me" example="after">...</div>]

setProp

setProp :: String -> String -> DOM Element -> Either Error DOM Element

Sets the value of the specified property on the passed in element(s).

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper">
 *   <h1 class="pick-me">Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 * <div class="wrapper">
 *   <h1 class="pick-me">Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 *
 */

// Change innerHTML on single element
const writeFirst = setProp('innerHTML', 'New Title');
writeFirst('.pick-me').map(console.log);
//> <h1 class="pick-me">New Title</h1>

// Change innerHTML class on multiple elements
const writeAll = compose(setProp('innerHTML', 'New Title'), domAll);
writeAll('.pick-me').map(console.log);
//> [<h1 class="pick-me">New Title</h1>, <h1 class="pick-me">New Title</h1>]

setStyle

setStyle :: String -> String -> DOM Element -> Either Error DOM Element

Sets the value of the specified style property on the passed in element(s).

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper">
 *   <h1 class="pick-me">Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 * <div class="wrapper">
 *   <h1 class="pick-me">Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 *
 */

// Change color on single element
const highlightFirst = setStyle('color', '#6A5ACD');
highlightFirst('.pick-me').map(console.log);
//> <h1 class="pick-me">New Title</h1>

// Change color on multiple elements
const highlightAll = compose(setStyle('color', '#6A5ACD'), domAll);
highlightAll('.pick-me').map(console.log);
//> [<h1 class="pick-me">New Title</h1>, <h1 class="pick-me">New Title</h1>]

toggleClass

toggleClass :: String -> DOM Element -> Either Error DOM Element

Toggles class from passed in element(s). If the class exists on the element it is removed. If the class doesn't exist on the element it is added

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper">
 *   <h1 class="pick-me green">Title</h1>
 *   <p class="pick-me green">Paragraph One</p>
 *   <p class="pick-me green blue">Paragraph Two</p>
 * </div>
 *
 */

// Toggle class on single element
const toggleClassBlue = toggleClass('blue');
toggleClassBlue('.pick-me').map(console.log);
//> <h1 class="pick-me green blue">Title</h1>

// Toggle class on multiple elements
const toggleClassBlueAll = compose(removeClass('blue'), domAll);
toggleClassBlueAll('.pick-me').map(console.log);
//> [<h1 class="pick-me green blue">Title</h1>, <p class="pick-me green blue">Paragraph One</p>, <p class="pick-me green">Paragraph Two</p>]

Getter Functions

getAttr

getAttr :: String β†’ DOM Element β†’ Either Error String

Returns the value of the passed in attribute. If attribute does not exist it returns an error.

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper pick-me" aria-expanded="false">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 * <div class="wrapper pick-me" aria-expanded="false">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 *
 */

// Get value of aria-expanded on single element
const getFirst = getAttr('aria-expanded');
getFirst('.pick-me').map(console.log);
//> "false"

// Get value of aria-expanded on multiple elements
const getAll = compose(getAttr('aria-expanded'), domAll);
getAll('.pick-me').map(console.log);
//> ["false","false"]

getClass

getClass :: Int β†’ DOM Element β†’ Either Error String

Returns the value of class at passed in index. If there is no class at the index it returns an error.

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper pick-me">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 * <div class="wrapper pick-me">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 *
 */

// Get value of aria-expanded on single element
const getFirst = getClass(1);
getFirst('.pick-me').map(console.log);
//> "wrapper"

// Get value of aria-expanded on multiple elements
const getAll = compose(getClass(1), domAll);
getAll('.pick-me').map(console.log);
//> ["wrapper","wrapper"]

getClasses

getClasses :: DOM Element β†’ Either Error [String]

Returns all classes on passed in element(s). If there are no classes it returns an error.

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper pick-me">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 * <div class="wrapper pick-me">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 *
 */

// Get value of aria-expanded on single element
const getFirst = getClasses(1);
getFirst('.pick-me').map(console.log);
//> ["wrapper", "pick-me"]

// Get value of aria-expanded on multiple elements
const getAll = compose(getClasses(1), domAll);
getAll('.pick-me').map(console.log);
//> [["wrapper", "pick-me"], ["wrapper", "pick-me"]]

getData

getData :: String β†’ DOM Element β†’ Either Error String

Returns the value of the passed in data-attribute. If data-attribute does not exist it returns an error. Gets value using the dataset property on DOM Elements so the data attribute you are getting will follow the naming rules outlined here: HTMLElement.dataset

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper pick-me" data-example="before">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 * <div class="wrapper pick-me" data-expample="before">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 *
 */

// Get value of data-example on single element
const getFirst = getData('example');
getFirst('.pick-me').map(console.log);
//> "before"

// Get value of data-example on multiple elements
const getAll = compose(setData('example'), domAll);
getAll('.pick-me').map(console.log);
//> ["before", "before"]

getProp

getProp :: String β†’ DOM Element β†’ Either Error String

Returns the value of the passed in property. If property does not exist it returns an error.

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper">
 *   <h1 class="pick-me">Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 * <div class="wrapper">
 *   <h1 class="pick-me">Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 *
 */

// Get innerHTML property on single element
const getFirst = getProp('innerHTML');
getFirst('.pick-me').map(console.log);
//> "Title"

// Get innerHTML property on multiple elements
const getAll = compose(getProp('innerHTML'), domAll);
getAll('.pick-me').map(console.log);
//> ["Title", "Title"]

getPosition

getPosition :: String β†’ DOM Element β†’ Either Error { bottom: Int, left: Int, right: Int, top: Int }

Returns an object representing the position of the passed in element(s) using the getBoundingClientRect method. It cherry picks the top, right, bottom, and left positionsa from the generated DOMRect instance.

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper pick-me">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 * <div class="wrapper pick-me">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 *
 */

// Get position of single element. Numbers are made up.
getPosition('.pick-me').map(console.log);
//> { top: 0, right: 200, bottom: 40, left: 0 }

// Get position multiple element. Numbers are made up.
const getAll = compose(getPosition, domAll);
getAll('.pick-me').map(console.log);
//> [{ top: 0, right: 200, bottom: 40, left: 0 }, { top: 40, right: 200, bottom: 80, left: 0 }]

getStyle

getStyle :: String β†’ DOM Element β†’ Either Error String

Returns the value of the passed in style property. If style property does not exist it returns an error. All styles fetched are generated by calling the window.getGeneratedStyles method.

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper">
 *   <h1 class="pick-me" style"color: #6A5ACD;">Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 * <div class="wrapper">
 *   <h1 class="pick-me" style"color: #6A5ACD;">Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 *
 */

// Get color on single element
const getFirst = getStyle('color');
getFirst('.pick-me').map(console.log);
//> "#6A5ACD"

// Get color on multiple elements
const getAll = compose(getStyle('color'), domAll);
getAll('.pick-me').map(console.log);
//> ["#6A5ACD", "#6A5ACD"]

serialize

serialize :: String β†’ DOM Element β†’ Either Error { name: value }

Returns object representing name/value pairs for all fields that are children of the passed in form.

/* IF THIS IS OUR MARKUP
 *
 * <form class="pick-me">
 *   <input type="text" name="first" value="Will" />
 *   <input type="text" name="last" value="King" />
 *   <input type="checkbox" name="check" value="true" />
 * </form>
 *
 */

serialize('.pick-me').map(console.log);
//> { first: "Will", last: "King", check: "true" }

Predicate Functions

hasAttr

hasAttr :: String β†’ DOM Element β†’ Either Error Bool

Returns boolean indicating if an attribute exists on the passed in element(s).

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper pick-me" aria-expanded="false">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 * <div class="wrapper pick-me" aria-expanded="false">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 *
 */

// Check if aria-expanded attribute exists on single element
const hasFirst = hasAttr('aria-expanded');
hasFirst('.pick-me').map(console.log);
//> true

// Check if aria-expanded attribute exists on multiple elements
const hasAll = compose(hasAttr('aria-expanded'), domAll);
hasAll('.pick-me').map(console.log);
//> [true, true]

hasClass

hasClass :: String β†’ DOM Element β†’ Either Error Bool

Returns boolean indicating if a class exists on the passed in element(s).

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper pick-me">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 * <div class="wrapper pick-me">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 *
 */

// Check if class exists on single element
const hasFirst = hasClass('wrapper');
hasFirst('.pick-me').map(console.log);
//> true

// Check if class exists on multiple elements
const hasAll = compose(hasClass('wrapper'), domAll);
hasAll('.pick-me').map(console.log);
//> [true, true]

hasData

hasData :: String β†’ DOM Element β†’ Either Error Bool

Returns boolean indicating if a data-attribute exists on the passed in element(s).

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper pick-me" data-example="before">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 * <div class="wrapper pick-me" data-expample="before">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 *
 */

// Check if data-example exists on single element
const hasFirst = hasData('example');
hasFirst('.pick-me').map(console.log);
//> true

// Check if data-example exists on multiple elements
const hasAll = compose(hasData('example'), domAll);
hasAll('.pick-me').map(console.log);
//> [true, true]

hasProp

hasProp :: String β†’ DOM Element β†’ Either Error Bool

Returns boolean indicating if a propert exists on the passed in element(s).

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper">
 *   <h1 class="pick-me">Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 * <div class="wrapper">
 *   <h1 class="pick-me">Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 *
 */

// Check if innerHTML property exists on single element
const hasFirst = hasProp('innerHTML');
hasFirst('.pick-me').map(console.log);
//> true

// Check if innerHTML property exists on multiple elements
const hasAll = compose(hasProp('innerHTML'), domAll);
hasAll('.pick-me').map(console.log);
//> [true, true]

hasStyle

hasStyle :: String β†’ DOM Element β†’ Either Error Bool

Returns boolean indicating if a style property exists on the passed in element(s).

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper">
 *   <h1 class="pick-me" style"color: #6A5ACD;">Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 * <div class="wrapper">
 *   <h1 class="pick-me" style"color: #6A5ACD;">Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 *
 */

// Check if color property exists on single element
const hasFirst = hasStyle('color');
hasFirst('.pick-me').map(console.log);
//> true

// Check if color property exists on multiple elements
const hasAll = compose(hasStyle('color'), domAll);
hasAll('.pick-me').map(console.log);
//> [true, true]

isAttr

isAttr :: String β†’ String β†’ DOM Element β†’ Either Error Bool

Returns boolean indicating if the passed in value matches the current value of an attribute on the passed in element(s).

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper pick-me" aria-expanded="false">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 * <div class="wrapper pick-me" aria-expanded="false">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 *
 */

// Check if aria-expanded value is "false" on single element
const isFirst = isAttr('aria-expanded', 'false');
isFirst('.pick-me').map(console.log);
//> true

// Check if aria-expanded value is "false"  on multiple elements
const isAll = compose(isAttr('aria-expanded', 'false'), domAll);
isAll('.pick-me').map(console.log);
//> [true, true]

isData

isData :: String β†’ String β†’ DOM Element β†’ Either Error Bool

Returns boolean indicating if the passed in value matches the current value of a data-attribute on the passed in element(s).

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper pick-me" data-example="before">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 * <div class="wrapper pick-me" data-expample="before">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 *
 */

// Check if data-example value equals "before" on single element
const isFirst = isData('example', 'before');
isFirst('.pick-me').map(console.log);
//> true

// Check if data-example value equals "before" on multiple elements
const isAll = compose(isData('example', 'before'), domAll);
isAll('.pick-me').map(console.log);
//> [true, true]

isProp

isProp :: String β†’ String β†’ DOM Element β†’ Either Error Bool

Returns boolean indicating if the passed in value matches the current value of a property on the passed in element(s).

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper">
 *   <h1 class="pick-me">Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 * <div class="wrapper">
 *   <h1 class="pick-me">Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 *
 */

// Check if innerHTML value equals "Title" on single element
const isFirst = isProp('innerHTML', 'Title');
isFirst('.pick-me').map(console.log);
//> true

// Check if innerHTML value equals "Title" on multiple elements
const isAll = compose(isProp('innerHTML', 'Title'), domAll);
isAll('.pick-me').map(console.log);
//> [true, true]

isStyle

isStyle :: String β†’ String β†’ DOM Element β†’ Either Error Bool

Returns boolean indicating if the passed in value matches the current value of an a style property on the passed in element(s).

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper">
 *   <h1 class="pick-me" style="color: #6A5ACD;">Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 * <div class="wrapper">
 *   <h1 class="pick-me" style="color: #6A5ACD;">Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 *
 */

// Check if color value equals "#6A5ACD" on single element
const isFirst = isStyle('color', '#6A5ACD');
isFirst('.pick-me').map(console.log);
//> true

// Check if color value equals "#6A5ACD" on multiple elements
const isAll = compose(isStyle('color', '#6A5ACD'), domAll);
isAll('.pick-me').map(console.log);
//> [true, true]

Event Functions

on

on :: String β†’ (event β†’ Either Error a) β†’ DOM Element β†’ DOM Element

Add an event listener to passed in element(s) by passing in the event you want to listen for and the callback function you want to run.

/* IF THIS IS OUR MARKUP
 *
 * <div class="wrapper pick-me" aria-expanded="false">
 *   <h1>Title</h1>
 *   <p>Paragraph One</p>
 * </div>
 * <button data-expand-wrapper>Click To Expand</button>
 *
 */

// Setup functions that will expand an element
const expand = setAttr('aria-expanded', 'true');
const expandOnClick = sel => e => expand(sel);

// Add click event to button with data-expand-wrapper data-attribute
on('click', expandOnClick('.pick-me'), '[data-expand-wrapper]');

Utility Functions

toBool

toBool :: String β†’ Either Error Bool

Function that takes the string representation of true and false boolean values and returns the actual boolean value. This is useful when you need an boolean when getting the value of an element attribute.

Note: Function takes both string versions of booleans and Either wrapped versions.

toBool('true').map(console.log));
//> true

toBool('false').map(console.log));
//> false

toBool(1).map(console.log));
//> error: "Argument 1 is not a String or Array String."

toBool(Either.of('true')).map(console.log));
//> true

toBool(Either.of('false')).map(console.log));
//> false

toBool(Either.of(1)).map(console.log));
//> error: "Argument 1 is not a String or Array String."

identity

identity :: a β†’ a

Function that just returns whatever is passed as the input.

console.log(identity(4));
//> 4

Build Your Own

If you are looking for how to wrap the core package with your own Data Type that info is located in the packages own README.

License

Apache-2.0

About

πŸ₯— Library of composable dom functions.

Resources

License

Stars

Watchers

Forks

Packages

No packages published