saladbar
v2.0.8
Published
Library of safe, composable dom functions.
Downloads
6
Maintainers
Readme
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
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.
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:
- Professor Frisby’s Introduces Composable Functional JavaScript
- Professor Frisby’s Mostly Adequate Guide To Functional Programming
- Master The JavaScript Interview: What Is Function Composition?
Examples
Play around with the API that solve the problems below.
Documentation
Table Of Contents
dom
: Grab a single element from the DOM. Equivalent toquerySelector
.domAll
: Grab all matching elements from the DOM. Equivalent toquerySelectorAll
.findParent
: Search all parent elements of passed in element based on a predicate function.
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.
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.
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.
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.