Easy to use JavaScript library for quick UI scripting.
dollar is a small, dependency-free, jQuery-style DOM helper. A single
global $() wraps a native DOM element (or a CSS selector, an HTML
snippet, or a list of elements) in a chainable instance with methods for
events, attributes, styling, class manipulation, and tree manipulation.
On top of that core it ships a handful of independent modules for common
needs: AJAX ($.get), JSON forms/POST ($.json), an HTML form builder
($.form), a virtualized/sortable data table ($.table), tooltips, and
a few form-field widgets (image cropper, click-to-edit, date picker,
autocomplete, a geo/map picker).
There is no build dependency and no package manager involved — you
either include the source files directly or build a minified bundle
with the provided Makefile.
- Getting started
- Building
- Core:
$() $.get— AJAX GET requests$.json— serialize forms / objects and POST them$.form— declarative form builder$.table— data table with sorting, virtualization and tree mode.tooltip()- Widgets
- Tests and examples
- License
Include the core file plus whichever modules you need, in this order
(later files extend $, so dollar.js must come first):
<script src="src/js/dollar.js"></script>
<script src="src/js/dollar.get.js"></script>
<script src="src/js/dollar.json.js"></script>
<script src="src/js/dollar.form.js"></script>
<script src="src/js/dollar.table.js"></script>
<script src="src/js/dollar.tooltip.js"></script>
<!-- optional widgets -->
<script src="src/js/widgets/date.js"></script>
<script src="src/js/widgets/autocomplete.js"></script>
<script src="src/js/widgets/image.js"></script>
<script src="src/js/widgets/clickedit.js"></script>
<script src="src/js/widgets/geo.js"></script>Each module attaches itself to window.$, so there's nothing to
initialize. $.form, $.table and the widgets that ship CSS also
expect their stylesheet (src/css/*.css, src/css/widgets/*.css) to be
loaded if you want the default look.
The Makefile concatenates and minifies everything into a single
release/dollar.min.js and release/dollar.min.css:
make # builds release/dollar.min.js and release/dollar.min.css
make clean # removes the release/ directoryRequirements:
uglifyjsonPATH, to compress and manglerelease/dollar.min.js(a source map is written alongside it asrelease/dollar.min.js.map).phponPATH, used to run the tiny minifier inopticss2.phpover the concatenated stylesheets.
The JS bundle is built from a fixed, order-sensitive file list defined
in the Makefile's SCRIPTS variable (core files first, then
src/js/widgets/*.js); the CSS bundle picks up everything under
src/css/ and src/css/widgets/. If you add a new core module, add it
to SCRIPTS explicitly — only the widgets/ directory is picked up
via wildcard.
$(argument) accepts several kinds of input and always returns either
a single wrapped-element instance, an array_instance (see
Collections), or null:
argument |
Behavior |
|---|---|
'#id' |
document.getElementById, wrapped. null if not found. |
'.class' |
document.getElementsByClassName, returned as a collection. |
'<tag>' or '<tag/>' |
Creates a new, empty <tag> element. |
'<div><span>1</span></div>' |
Parses HTML; returns the single root element wrapped, or a collection if the fragment has multiple top-level elements. |
| any other string | Treated as a tag name: document.getElementsByTagName(text), returned as a collection. |
a DOM element / Node / DocumentFragment |
Wrapped directly (repeated calls on the same node return the same wrapper instance). |
an array-like of elements (e.g. NodeList, HTMLCollection) |
Wrapped as a collection. |
falsy value ('', null, undefined, 0) |
Returns null. |
| an already-wrapped instance | Returned as-is. |
$('#header'); // element by id
$('.tab'); // collection of elements by class
$('div'); // collection of all <div>s
$('<span/>'); // new, detached <span>
$('<div class="x">hi</div>'); // new, detached <div class="x">hi</div>
$(document.body); // wrap an existing native nodeEvery wrapped instance exposes the underlying DOM node as .native.
When $() resolves to more than one element you get an array_instance:
an array-like object (.length, numeric indices 0..length-1, each
already wrapped) that also exposes every instance method (see below).
Calling a method on a collection calls it on each element in turn and
does not return a chainable value — use it for side effects
($('.tab').hide(), $('.item').addClass('done')), not for reading a
single value back.
$('input').val(''); // clears every input's value
$('.row').each(function (el) {
console.log(el.text());
});.each(iteratee) iterates the collection, calling iteratee(element)
for each wrapped element.
All of these are chainable (return this) unless noted as "getter".
Calling a getter/setter method with no arguments reads the value;
calling it with an argument sets it and returns this — this
argument-count check is intentional, so passing undefined explicitly
(e.g. $('input').text(undefined)) is still treated as a set, not a
read.
Events
.event(name, handler, capture?)— attach a listener (handleris called withthisbound to the wrapped instance). Falls back toattachEventon old IE.- Shorthands:
.click(),.keydown(),.keypress(),.keyup(),.mousedown(),.mousemove(),.mouseup(),.input()— each is.event(name, handler). .trigger(eventName, eventAttrs?)— dispatch a syntheticHTMLEventsevent, optionally copying extra properties fromeventAttrsonto the event object..submit(handler?)— with a handler, same as.event('submit', ...); with no argument, calls the native.submit()..ready(handler)— runshandleronce the document has finished loading (fires immediately if it already has). Typically called as$(document).ready(fn).
Value / state
.val(value?)— get/set.value(inputs, textareas, selects)..checked(value?)— get/set.checked(checkboxes/radios)..attr(name, value?)/.attr(object)— get/set an attribute. Settingvaluetonullremoves the attribute. Passing an object sets each key/value pair..css(name, value?)/.css(object)— get/set an inline style. Accepts dash-case ('font-size') or camelCase. Setting a falsy value removes the property. Passing an object sets each key/value pair..show()/.hide()/.toggle()— setdisplay: block/none, or flip between them..text(text?)— get/settextContent..tag()— lower-cased tag name (getter only)..focus()— calls native.focus().
Classes (uses classList when available, falls back to manual
class-attribute parsing otherwise)
.addClass(name).removeClass(name).hasClass(name)— getter, returns boolean..toggleClass(name)
Tree manipulation
.append(child)— appends a wrapped element, a native node, or a plain string (inserted as a text node)..prepend(child)— same, but inserts as the first child..prev(sibling?)— with an argument, insertssiblingimmediately before this element; with none, returns the previous sibling wrapped, orfalseif there isn't one..next(sibling?)— with an argument, insertssiblingimmediately after this element; with none, returns the next sibling wrapped, orfalseif there isn't one..parent()— wrapped parent node, ornull..remove()— removes the element from its parent..empty()— removes all children (replaceChildren()when available, elseinnerHTML = '')..children(index?)— with no argument, returns all children as a collection; with a numeric index, returns that one child (wrapped) ornull; with a string starting with., returns descendants matching that class name (getElementsByClassName)..cell(row, column)— convenience for<table>elements: returns the wrapped<td>/cell at(row, column)inside the<tbody>..clone()— deep-clones the node (and strips itsid, if any) and returns the wrapped copy..each(itemHandler)— on a single instance, just callsitemHandler(this)once (on a collection it iterates all elements — see Collections).
$.defined(value)/$.undefined(value)—=== undefined/!== undefinedchecks (used pervasively instead oftypeof x == 'undefined').$.extend(name, fn)— registersfnas a new instance method namedname, available both on single instances and (automatically) on collections. This is how every method above and every module in this library is added; use it the same way to add your own.$.array(elements?)— turns a plain array (orundefined, giving a new empty array) into an "enhanced" array with.add(),.remove(index, count?),.append(),.prepend(), and.each(itemHandler)helpers. Calling it twice on the same array is a no-op (idempotent, tagged withjsDollarArray).$.debounce(fn, timeout=400)— returns a debounced wrapper aroundfn(waitstimeoutms of inactivity before callingfn; supports up to 9 forwarded arguments).$.spread(original, ...sources)— returns a new value,originalis not mutated: for an arrayoriginal, returnsoriginal.concat(...sources); for an object, returns a shallow merge oforiginaland...sources(Object.assign({}, original, ...sources)).$.widgets— namespace object where widget constructors are registered (see Widgets).
$.extend('fadeOut', function () {
this.css('opacity', '0');
return this; // keep it chainable
});
$('#panel').fadeOut();(from src/js/dollar.get.js)
$.get('/api/items')
.success(function (responseText) { /* ... */ })
.error(function ({ status, statusText }) { /* ... */ });$.get(url) fires an async XMLHttpRequest GET (deferred one tick via
setTimeout so you can attach .success/.error after the call
returns) and returns an object with:
.success(handler)— called with the raw response text on HTTP 200..error(handler)— called with{status, statusText}on any other status.
Both are optional and chainable on the returned instance.
(from src/js/dollar.json.js)
$.json(source) normalizes source into a serializable object and
returns it (augmented with .toString(), .toObject(), and .post()).
source can be:
- A
<form>element (native or wrapped) — reads every namedinput/select/textareainside it into a plain object. Checkboxes with avalueattribute collect into an array under their sharedname; valueless checkboxes become a boolean; radios contribute their checked value; multi-selects and file inputs are not read (use a widget, e.g.$.widgets.image, for file uploads). - A JSON string — kept verbatim for
.toString(), parsed lazily by.toObject(). - A plain object —
.toString()returnsJSON.stringify(this).
var data = $.json($('#myForm'));
data.toObject(); // -> { name: 'Ana', accepts_tos: true, ... }
data.toString(); // -> '{"name":"Ana","accepts_tos":true,...}'.post(url, options?) POSTs data.toString() to url with
Content-Type: text/json, and returns a chainable instance:
data.post('/api/save', { postButton: $('#submit'), postButtonCaption: 'Saving…' })
.success(function (parsedResponseOrText) { /* ... */ })
.error(function ({ status, statusText }) { /* ... */ });options.postButton— a wrapped button; it's disabled for the duration of the request and re-enabled afterwards, restoring its original caption (or showingoptions.postButtonCaptionwhile the request is in flight).- The success handler receives the response parsed as JSON via
$.json(responseText).toObject()when possible, falling back to the raw text. .cancel()aborts the in-flight request and resets the button.
(from src/js/dollar.form.js, styled by src/css/form.css)
Builds a <form><dl>...</dl></form> (one <dt>/<dd> pair per field)
from a field-descriptor array, and wires up basic client-side
validation plumbing.
var form = $.form([
{ name: 'first_name', caption: 'First name', type: 'text', size: 20 },
{ name: 'accepts_tos', caption: '', type: 'checkbox', label: 'I accept the terms' },
{ name: 'country', caption: 'Country', type: 'select', options: { ar: 'Argentina', us: 'USA' } },
{ name: 'save', type: 'submit', value: 'Save' }
], { first_name: 'Ana', country: 'ar' }); // defaults, keyed by field name
$(document.body).append(form);Field descriptor properties:
| Property | Meaning |
|---|---|
name |
Field name (name= attribute, and defaults/values key). |
caption |
Text placed in the field's <dt>. |
type |
'select', 'checkbox', 'radio', 'textarea', 'submit', or anything else (rendered as <input type="...">, e.g. 'text', 'password', 'hidden'). |
options |
For type: 'select': an object of { value: label }. |
value |
For checkbox/radio/submit: the input's value. |
placeholder |
Sets the placeholder attribute, if given. |
size |
Sets the size attribute on plain inputs. |
inline |
Adds the dollar-form-inline class to the <dt>. |
label / label_class |
Wraps the input in a <label> (text or another wrapped element as its content). |
widget |
A function function (input) { ... } (see Widgets) invoked with the created input; its return value is stored and reachable via form.widget(name). |
defaults is an object keyed by field name, used to pre-fill values
(and to call widget.set(defaults[name]) when a matching widget is
attached and the default is an object).
Returned form object (itself a wrapped <form>) exposes:
-
form.input(name)— the wrapped<input>/<select>/<textarea>. -
form.widget(name)— the object returned by that field'swidget()factory, if any. -
form.error(name, message)— showsmessageunder fieldnameand marks the form as having an error (used from inside a.validate()handler). -
form.validate(handler)— install a submit-time validator.handlerruns on everysubmitevent; it should callform.error(name, msg)for each invalid field. If no errors were raised, submission continues normally; after calling.validate(),form.submit()no longer submits the form natively but instead registers post-validation submit handlers (form.submit(function (e) {...})), which is$.json/AJAX-friendly:form.validate(function () { if (!form.input('first_name').val()) { form.error('first_name', 'Required'); } }); form.submit(function (e) { e.preventDefault(); $.json(form).post('/api/save').success(function () { /* ... */ }); });
Separately, dollar.form.js also installs a page-wide keydown
handler (on document.ready) so pressing Enter in a form field moves
focus to the next visible input/select/textarea instead of
submitting, skipping submit/button/reset inputs, textareas, and
autocomplete-widget fields.
(from src/js/dollar.table.js, styled by src/css/table.css)
var table = $.table({ fix_headers: true }, [
{ name: 'name', caption: 'Name', sortable: true },
{ name: 'surname', caption: 'Surname', sortable: true },
{ name: 'birthday', caption: 'Birthday', sortable: true, align: 'right',
format: function (birthday) { return new Date(birthday).toLocaleDateString(); } }
], people); // array of row objects
$(document.body).append(table);$.table(options?, columns, data) — options may be omitted (defaults
to {}; then columns shifts into options's position, so it's
safe to call $.table(columns, data) too).
Column descriptor:
| Property | Meaning |
|---|---|
name |
Key read from each row object for this column's raw value. |
caption |
Header text. Omit for an unlabeled column (e.g. a tree-toggle column). |
format |
function (value) (if name is set) or function (row) (if not) returning the text/HTML to display. Returning a wrapped element (or anything with a .native) appends it instead of setting text. |
align |
CSS text-align for the column's cells. |
sortable |
true to sort using natural/locale-aware string+number comparison, or a custom function (a, b, reverse) comparator. |
Options:
| Property | Meaning |
|---|---|
tree |
Enables tree mode: rows link to a parent via row.parent_id/row.id, an extra +/- toggle column is auto-prepended, and children start collapsed (dollar-table-collapsed). |
fix_headers |
Keeps the header row pinned to the top of the viewport while scrolling. |
format_row |
function (tr, row), called after each row renders, for custom per-row tweaks (wrapped in try/catch). |
sortable_use_text (deprecated) |
Sort by the rendered cell text instead of the underlying data; incompatible with tree. |
Notable behavior:
- Rows are rendered lazily/virtualized: only enough rows to fill
the visible viewport (plus a lookahead) are rendered as you scroll,
driven by a 40ms polling interval that measures the table's scroll
position.
Ctrl+Fand theEndkey force a full flush. - Right-clicking the header opens a small per-column show/hide menu
(
.dollar-table-menu). - Hovering a sortable header reveals ascending/descending sort icons.
table.flush(callback?)— force all remaining rows to render, then callcallback.table.sort(columnIndex, reverse?)— sort by column index programmatically.table.remove()— removes the table and tears down its interval timer and scroll listeners (always use this instead of the generic.remove()when discarding a table, to avoid leaking thesetInterval).
(from src/js/dollar.tooltip.js)
$('#info-icon').tooltip('Click to learn more');Attaches a shared, single-instance tooltip <div> (appended to
document.body once, positioned absolutely) that appears on
mouseover and disappears on mouseout, with a short grace period so
moving the mouse into the tooltip itself (e.g. to click a link) keeps
it open. text may be a plain string or a wrapped element/fragment for
rich content. Calling .tooltip(newText) again on the same element
just updates its text; the event handlers are attached once.
Widgets are functions registered under $.widgets that take a wrapped
<input> and progressively enhance it into a richer control, usually
by hiding the original input (type="hidden") and inserting a visible
UI next to it that keeps the hidden input's value in sync. Pass a
widget as a form field's widget property (see $.form)
or call it directly: $.widgets.xyz($('#myInput'), ...).
(styled by src/css/widgets/date.css)
$.widgets.date($('#birthday'));Turns the input (expects/produces YYYY-MM-DD) into a hidden field
plus a read-only display input, an edit button, and a popup month/year
calendar (localized to en/es based on navigator.language, falling
back to en). Clicking a day sets both the hidden value and the
human-readable display.
(styled by src/css/widgets/autocomplete.css)
// static list of options, keyed by value:
$.widgets.autocomplete($('#country'), { ar: 'Argentina', us: 'USA' });
// or async lookup:
$.widgets.autocomplete($('#user'), function (text, respond) {
$.get('/api/users?q=' + encodeURIComponent(text))
.success(function (json) { respond(JSON.parse(json)); });
});$.widgets.autocomplete(input, options, display_text?, settings?):
options— either an object of{ value: label }, or afunction (text, respond)for server-backed lookups (respondis called with a results object in the same{ value: label }shape).display_text— initial text shown in the visible field.settings.autocomplete_only— if true, free text typed by the user is kept as the value even without selecting a suggestion (adds thedollar-widget-autocomplete-onlyclass).
Supports arrow-key navigation and Enter/Escape in the results popup,
caps suggestions at 20 results, and auto-selects when exactly one
result matches. Returns an object with set({value, display}),
caption(text?), focus(), enable(), disable(), and display()
(the wrapped visible input).
$.widgets.image($('#avatar'), 200, 'image/jpeg', 0.85);$.widgets.image(input, size, type?, quality?) turns the input into an
image picker/cropper: a native file input, a size × size preview box
you can drag to reposition, and a rotate button. On any change it
re-encodes the (possibly downsampled) image as a data URL of type
(default determined by canvas.toDataURL's own default) at quality
and stores it as the hidden input's value. Touch dragging is supported
alongside mouse dragging.
$.widgets.clickedit($('#name'));Shows the current value as plain text with an edit button; clicking the
button reveals a real text input in place. Returns { set(value) }.
(Note: this widget's set() currently references an undefined span
variable and a hardcoded img/edit.png icon path — check this against
your needs before relying on it as-is.)
$.widgets.geo($('#coordinates')); // input value is "lat,lng"Renders an interactive OpenLayers 3 map with
a draggable marker, lazily loading ol.js/ol.css from cdnjs if not
already present. Updates the input's "lat,lng" value (and fires a
change event) as the marker is moved. Requires network access to the
CDN unless OpenLayers is already loaded on the page.
tests/dollar.html— a small hand-rolled assertion suite (seetests/test.jsfor thetest()/assert()helpers) covering the core$()API (element creation,val,text,checked,attr,css,show/hide/toggle). Open it directly in a browser to run it; it loadssrc/js/dollar.jsunbundled. Not yet covered (see the comment at the bottom of the file):children,each,append,parent,remove,prepend,prev,next,empty, class methods,focus,cell,clone,$.array,$.debounce,$.spread.examples/table.html— a runnable example of$.tablewith generated sample data, sortable columns, and a date formatter. Runmakefirst sorelease/dollar.min.js/.cssexist, then open the file in a browser.
MIT.