Skip to content

Latest commit

 

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dollar

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.

Contents

Getting started

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.

Building

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/ directory

Requirements:

  • uglifyjs on PATH, to compress and mangle release/dollar.min.js (a source map is written alongside it as release/dollar.min.js.map).
  • php on PATH, used to run the tiny minifier in opticss2.php over 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.

Core: $()

Selecting / creating elements

$(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 node

Every wrapped instance exposes the underlying DOM node as .native.

Collections

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.

Instance methods

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 (handler is called with this bound to the wrapped instance). Falls back to attachEvent on old IE.
  • Shorthands: .click(), .keydown(), .keypress(), .keyup(), .mousedown(), .mousemove(), .mouseup(), .input() — each is .event(name, handler).
  • .trigger(eventName, eventAttrs?) — dispatch a synthetic HTMLEvents event, optionally copying extra properties from eventAttrs onto the event object.
  • .submit(handler?) — with a handler, same as .event('submit', ...); with no argument, calls the native .submit().
  • .ready(handler) — runs handler once 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. Setting value to null removes 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() — set display: block / none, or flip between them.
  • .text(text?) — get/set textContent.
  • .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, inserts sibling immediately before this element; with none, returns the previous sibling wrapped, or false if there isn't one.
  • .next(sibling?) — with an argument, inserts sibling immediately after this element; with none, returns the next sibling wrapped, or false if there isn't one.
  • .parent() — wrapped parent node, or null.
  • .remove() — removes the element from its parent.
  • .empty() — removes all children (replaceChildren() when available, else innerHTML = '').
  • .children(index?) — with no argument, returns all children as a collection; with a numeric index, returns that one child (wrapped) or null; 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 its id, if any) and returns the wrapped copy.
  • .each(itemHandler) — on a single instance, just calls itemHandler(this) once (on a collection it iterates all elements — see Collections).

Static helpers on $

  • $.defined(value) / $.undefined(value)=== undefined / !== undefined checks (used pervasively instead of typeof x == 'undefined').
  • $.extend(name, fn) — registers fn as a new instance method named name, 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 (or undefined, 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 with jsDollarArray).
  • $.debounce(fn, timeout=400) — returns a debounced wrapper around fn (waits timeout ms of inactivity before calling fn; supports up to 9 forwarded arguments).
  • $.spread(original, ...sources) — returns a new value, original is not mutated: for an array original, returns original.concat(...sources); for an object, returns a shallow merge of original and ...sources (Object.assign({}, original, ...sources)).
  • $.widgets — namespace object where widget constructors are registered (see Widgets).

Extending $ with new methods

$.extend('fadeOut', function () {
    this.css('opacity', '0');
    return this;   // keep it chainable
});

$('#panel').fadeOut();

$.get — AJAX GET requests

(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.

$.json — serialize forms / objects and POST them

(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 named input/select/textarea inside it into a plain object. Checkboxes with a value attribute collect into an array under their shared name; 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() returns JSON.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 showing options.postButtonCaption while 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.

$.form — declarative form builder

(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's widget() factory, if any.

  • form.error(name, message) — shows message under field name and marks the form as having an error (used from inside a .validate() handler).

  • form.validate(handler) — install a submit-time validator. handler runs on every submit event; it should call form.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.

$.table — data table with sorting, virtualization and tree mode

(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+F and the End key 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 call callback.
  • 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 the setInterval).

.tooltip()

(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

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'), ...).

$.widgets.date

(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.

$.widgets.autocomplete

(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 a function (text, respond) for server-backed lookups (respond is 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 the dollar-widget-autocomplete-only class).

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

$.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

$.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

$.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 and examples

  • tests/dollar.html — a small hand-rolled assertion suite (see tests/test.js for the test()/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 loads src/js/dollar.js unbundled. 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 $.table with generated sample data, sortable columns, and a date formatter. Run make first so release/dollar.min.js/.css exist, then open the file in a browser.

License

MIT.

About

Easy to use Javascript library for quick UI scripting

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages