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.
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
.
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
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
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.
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.
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.
/** 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');
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
orquerySelectorAll
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.
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:
- Professor Frisbyβs Introduces Composable Functional JavaScript
- Professor Frisbyβs Mostly Adequate Guide To Functional Programming
- Master The JavaScript Interview: What Is Function Composition?
Play around with the API that solve the problems below.
Grabbing elements from the DOM
Setter Functions
addClass
: Add class/classes on all passed in Elements.removeClass
: Remove class/classes on all passed in Elements.replaceClass
: Replace a class with another class on all passed in Elements.setAttr
: Set the value of an attribute on all passed in Elements.setData
: Set the value of a data-attribute on all passed in Elements.setProp
: Set the value of a property on all passed in Elements.setStyle
: Set the value of a style-property on all passed in Elements.toggleClass
: Add a class on all passed in Elements if it doesnβt already exist or remove it if it does.
Getter Functions
getAttr
: Return the value of an attribute from all passed in elements.getClass
: Return the class at the index from all passed in elements.getClasses
: Return all the classes from all passed in elements.getData
: Return the value of a data-attribute from all passed in elements.getProp
: Return the value of a property from all passed in elements.getPostion
: Return object representing the postion of all passed in elements.getStyle
: Return the value of a style-property from all passed in elements.serialize
: Return name and value pairs for all inputs of the passed in form as a data object.
Predicate Functions
hasAttr
: Check if an attribute exists for all passed in elements.hasClass
: Check if a class exists for all passed in elements.hasData
: Check if a data-attribute exists for all passed in elements.hasProp
: Check if a property exists on all passed in elements.hasStyle
: Check if a style-property exists on all passed in elements.isAttr
: Check if an attributeβs value matches the passed in value for all passed in elements.isData
: Check if a data-attributeβs value matches passed in value for all passed in elements.isProp
: Check if a propertyβs value matches passed in value for all passed in elements.isStyle
: Check if a style-propertyβs value matches passed in value for all passed in elements.
Event Functions
Utility Functions
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.
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.
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 :: (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 :: 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>]
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 :: 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 :: 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 :: 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 :: 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 :: 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 :: 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 :: 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>]
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 :: 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 :: 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 :: 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 :: 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 :: 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 :: 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 :: 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" }
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 :: 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 :: 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 :: 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 :: 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 :: 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 :: 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 :: 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 :: 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]
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]');
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 :: a β a
Function that just returns whatever is passed as the input.
console.log(identity(4));
//> 4
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.