A small (2.2kb gzipped), Backbone-inspired, library for greater organization on small projects.
Washi is available on npm:
npm install --save washi
If npm isn't your thing, or you'd rather include Washi as vendor code, download it from the unpkg
CDN:
This is a UMD module. It should interop perfectly with CommonJS and AMD, or otherwise as a global by accessing Washi
.
var Washi = require('washi');
var $ = Washi.$;
var Sample = {
ui: {
title: '.title',
},
events: {
// Use ui helper selectors as aliases with {element}
'mousedown {title}, touchstart {title}' : 'doSomething',
// Alternatively, follow the syntax used by Backbone.Marionette
'click @ui.title': 'doSomethingElse'
},
initialize: function(options) {
this.ui.title.forEach(function(e) {
e.innerHTML = 'Washi is for Origami'
});
},
doSomething: function() {
var text = $.map(this.ui.title, el => el.innerHTML).join(' ')
console.log(text)
},
doSomethingElse: function() {
console.log("Something else")
}
}
var sample = Washi(Sample, {
el: "#sample-el"
})
Corresponding with:
<div id="sample">
<h1 class="title">Paper Crane</h1>
</div>
Create a view component based upon a configuration:
let Sample = {
ui: {
title: '.title',
},
events: {
'click @ui.title': 'doSomething'
},
initialize: function(options) {
// Startup
console.log("Hello, world!")
},
doSomething: function() {
alert("click!")
}
}
let sample = Washi(Sample, {
el: "#sample-el"
})
Invoked after the Washi instance has been created. Use this method for setup behavior.
let Widget = {
initialize: function(options) {
console.log('all set!')
}
}
let component = Washi(Widget, { el: document.body }) // "all set!"
Attach event listeners to the element associated with the component. Keys in this object are selectors, values are string references to methods on the configuration object:
let Widget = {
events: {
'click button': 'onClick'
},
onClick: function(event) {
alert("HELLO!")
}
}
let component = Washi(Widget, { el: '.selector' })
component.query('button').invoke('click') // "HELLO!"
Preselect child nodes within the element provided to Washi. These selections are available in a few places.
In the events object, @ui.{name}
is replaced with the CSS selector for the UI entry:
let Widget = {
ui: {
'button': 'button'
},
events: {
'click @ui.button': 'onClick'
},
onClick: function(event) {
alert("HELLO!")
}
}
Reference the selected elements associated with a ui
entry under this.ui
:
let Widget = {
ui: {
'button': 'button'
},
initialize: function() {
// An array of DOM elements
this.ui.button.forEach(el => el.click())
// Alternatively, a Washi chain
this.ui.$button.invoke('click')
}
}
Washi exposes a number of utility methods under the $
namespace:
- chain
- Array operations
- toArray
- extend
- has
- query
- queryAll
- invoke
- isBlank
- isDOM
- isFunction
- isObject
- isRegExp
- isString
- isUndefined
- matches
- on
- off
- result
- tap
- template
- append
- remove
- addClass
- removeClass
- toggleClass
Creates a pipeline for executing multiple operatins on a value, this can be a single object or a list of values. Use valueOf()
to end the chain, returning the underlying value:
const $ = Washi.$
let chain = $.chain([1,2,3]).map(i => i + 1)
let result = chain.valueOf()
console.log(result) // [2,3,4]
Passing a string to Washi.$
executes document.querySelectorAll
, converting the selection into a chain:
const $ = Washi.$
$('button').on('click', () => {
alert("HI!")
})
Washi.$ includes most ES5 methods from the Array prototype. The primary benefit of doing this is to work around DOM APIs that return "array like" values such as NodeList
:
join
reverse
sort
push
pop
shift
unshift
slice
splice
concat
indexOf
lastIndexOf
forEach
map
reduce
reduceRight
filter
some
every
You can use these directly, however they are primary intended for use with chaining:
const $ = Washi.$
let internalLinks = $('a').filter(el => el.href.origin === window.location.origin)
internalLinks.on('click', () => console.log("Retention! Hurrah!"))
Convert a list-like value, such as a NodeList
returned from document.querySelectorAll
into an array:
const $ = Washi.$
let links = document.querySelectorAll('a')
let hrefs = $.toArray(links).map(el => el.href)
Given a list of arguments, extend a target object with additional properties:
const $ = Washi.$
let red = { r: 200, g: 0, b: 0 }
let blue = { r: 0, g: 0, b: 220 }
let purple = $.extend({}, red, blue) // { r: 200, g: 0, b: 200 }
Important: the first argument, target
, is mutated. This is a destructive operation!
Returns true if a given object has a property. This is sugar around Object.prototype.hasOwnProperty
that covers some edge cases, such as null or undefined values.
const $ = Washi.$
let styles = { color: 'blue', font: '14px Helvetica' }
$.has(styles, 'color') // true
$.has(styles, 'border') // false
Select an HTML element. When no second argument is given, selection occurs on the document
. When no element is found, it returns null
.
const $ = Washi.$
let btn = $.query('button')
if (btn) {
btn.click()
}
Select multiple HTML elements. The resulting selection is converted into an Array, making its safe to perform operations like forEach
, map
, and reduce
. When no element is found, it returns an empty array.
const $ = Washi.$
let items = $('ul').queryAll('li')
items.forEach(el => console.log(item.innerHTML))
Execute a method on each member of a list:
const $ = Washi.$
// Operate on data
let planets = ['Mercury', 'Venus', 'Earth', 'Mars']
$.invoke(planets, 'toUpperCase') // ['MERCURY', 'VENUS', 'EARTH', 'MARS']
// Or call methods on a list of elements
$('button').invoke('click')
Is a value null or undefined?
const $ = Washi.$
$.isBlank('') // false
$.isBlank(false) // false
$.isBlank(0) // false
$.isBlank(null) // true
$.isBlank(undefined) // true
Is a value a DOM element?
const $ = Washi.$
$.isDOM({}) // false
$.isDOM(new Image()) // true
Is a value a function?
const $ = Washi.$
$.isFunction({}) // false
$.isFunction(function() {}) // true
Is a value an object? This function helps to avoid pitfalls with type checking objects. For example: typeof null === 'object'
!
const $ = Washi.$
$.isObject({}) // true
$.isObject([]) // true
$.isObject(null) // false
$.isObject(function() {}) // false
Is a value a regular expression?
const $ = Washi.$
$.isRegExp({}) // false
$.isRegExp(/[A-Z]/) // true
$.isRegExp(new RegExp("[A-Z]")) // true
Is a value a string?
const $ = Washi.$
$.isString({}) // false
$.isString("Hello world") // true
Is a value undefined?
const $ = Washi.$
$.isUndefined(null) // false
$.isUndefined('') // false
$.isUndefined(0) // false
$.isUndefined(undefined) // true
Returns true if the provided element matches a CSS selector. When possible, this function uses the Element.matches DOM API.
const $ = Washi.$
$.matches(document.body, '.class-name')
Safely attach an event listener with extreme browser support (IE5+).
const $ = Washi.$
$.on(document.body, 'click', event => alert("CLICK!"))
// Or chain it:
$('button').on('click', event => alert("CLICK!"))
For more information about event listening, see EventTarget.addEventListener
Safely remove an event listener with extreme browser support (IE5+).
const $ = Washi.$
let buttons = $('button')
let handler = event => alert("CLICK!")
buttons.on('click', handler)
buttons.invoke('click') // CLICK! CLICK! CLICK!
buttons.off('click', handler)
buttons.invoke('click') // silence
Check for ownership of a value, optionally calling it if it is a function. If the value is undefined
, return the fallback value.
const $ = Washi.$
$.result({}, 'method', 'missing') // 'missing'
$.result('foobar', 'toUpperCase', 'UNKNOWN') // 'FOOBAR'
Calls a function at a given scope, passing in the target value as the only argument. This is primarily intended side-effects when chaining:
const $ = Washi.$
function render(items) {
let p = document.createElement('p')
p.innerHTML = items.join(', ')
document.body.appendChild(p)
}
let dates = $([new Date('01-01-2000'), new Date('02-02-2010'])
An extremely simple templating language. Primary used for basic string replacement. For more serious uses, particulary with DOM manipulation, use a vetted templating language such as mustachejs.
const $ = Washi.$
$.template('{foo}', { foo: 'bar' }) //=> 'bar'
$.template('{foo}') //=> '{foo}'
Append a list of children to an element:
const $ = Washi.$
let planets = $(['Mercury', 'Venus', 'Earth', 'Mars'])
planets.map(item => {
var el = document.createElement('li')
el.innerHTML = item
return el
})
$('ul').append(planets.valueOf())
Remove a single HTML element from its parent, or each element within a list:
const $ = Washi.$
// Remove buttons from a form:
$('#my-form button').remove()
Add one or more class names to an element:
const $ = Washi.$
let el = document.createElement('button')
el.className = 'btn'
$.addClass(el, 'btn-large')
console.log(el.className) // 'btn btn-large'
Remove one or more class names to an element:
const $ = Washi.$
let el = document.createElement('button')
el.className = 'btn btn-large'
$.removeClass(el, 'btn btn-large')
console.log(el.className) // ''
Toggle one or more class names to an element. When provided, adds/removes the class names based upon the keep
argument
const $ = Washi.$
let el = document.createElement('button')
el.className = 'btn'
$.toggleClass(el, 'active')
console.log(el.className) // 'btn active'
$.toggleClass(el, 'active')
console.log(el.className) // 'btn'
$.toggleClass(el, 'active', false)
console.log(el.className) // 'btn'
$.toggleClass(el, 'active', true)
console.log(el.className) // 'btn active'
Visit code.viget.com to see more projects from Viget.