Class in Sqimitive jQuery

class Sqimitive.jQuery extends Sqimitive.Base
A Sqimitive with a DOM representation via the jQuery interface (jq:)

Defined in: sqimitive-jquery.js, line 52

Description (skip)

A Sqimitive with a DOM representation via the jQuery interface (jq:).

Sqimitive\jQuery is the base class used in a typical web application. It turns el into a jQuery node, keeps it attached to _parent’s el, maintains el’s event listeners in DOM, etc.

This class can also work with libraries imitating jQuery API like Zepto since this is a thin interface to jQuery and very few of its features are actually used.

See the included sample To-Do application for a “real-world” example.

Examplejqex
var Task = Sqimitive.jQuery.extend({
  el: {tag: 'li'},

  _opt: {
    // Keep el appended to the parent's el.
    attachPath: '.',
    // Define the default attributes of Task:
    caption: '',
    done: false,
  },

  events: {
    // Update the visual presentation whenever any _opt changes:
    change: 'render',

    render: function () {
      this.el.text(this.get('caption'))
        .toggleClass('done', this.get('done'))
    },
  },

  elEvents: {
    // Listen for double clicks on this.el to change the done state:
    dblclick: function () {
      this.set('done', !this.get('done'))
    },
  },
})

// Create a parent container for our to-do items (Task's) which
// is placed into the DOM at $('#tasks'):
var list = new Sqimitive.jQuery({el: '#tasks'})

// Create a new item, add it to list's _children:
list.nest( new Task({caption: 'Ready steady go!'}) )
// ...append to list's el and render for the first time:
  .attach().render()

// Double click on Task's element in the window to change its
// className.

Properties

$

Modifiers: static

Holds reference to the global $ object.

var body = Sqimitive.jQuery.$('body')
var p = Sqimitive.jQuery.$('<p class=text>')

A similar global property Sqimitive._ holds reference to the utility library in use.

Defined in: sqimitive-jquery.js, line 394Show code

$: $,
_opt

Modifiers: protected

New standard options (Base._opt):

Members
Name Types Notes
attachPathstring selector, object DOM or jQuery Default root to where this el is appended, resolved via _parent’s $() or, if there’s no parent, via global $(). Used when attach() is called without arguments (happens when _parent’s attach() or render() is called).

Usually the value is a string selector like form > .filters or a period . (special case when have a _parent, gets its el).

elstring There is no such option but if el is given to new as part of options then it replaces the declaration-time value of the el property – see el for details.
object

Defined in: sqimitive-jquery.js, line 123Show code

_opt: {},
el

from setOnDecl

May only be set upon declaration via Core::extend() or mixIn.

Holds a DOM node assigned to this object or null.

Example
// Set this.el by selector; becomes $(document.body):
new Sqimitive.jQuery({el: 'body'})
// Set this.el to a jQuery node:
new Sqimitive.jQuery({el: $('body')})
// Set this.el by a DOM node; becomes wrapped into $() by init():
new Sqimitive.jQuery({el: document.body})
// Create new element via string specification for $():
new Sqimitive.jQuery({el: '<p class=text>'})
// Create new element <p class="text"> by object:
new Sqimitive.jQuery({el: {tag: 'p', className: 'text'}})
// Cancel node creation, el becomes null:
new Sqimitive.jQuery({el: false})

// WRONG: when changing after construction el must always be a
// jQuery node, only and ever:
sqim.el = '<p class=text>'
// WRONG: result is a DOM node, not a jQuery node:
sqim.el = document.createElement('p')
// CORRECT: result is a jQuery node:
sqim.el = $('<p class=text>')

el’s value is special during construction: first, init() takes the value of either the el key from new’s options or, if missing, of this property, then sets el’s property value according to it:

Members
Name Types Notes
false Assumes null el; no DOM node is created or assigned - useful for pure data structures aka “models” (but then you might not need Sqimitive\jQuery at all, use the lighter Base).
string A se #lec > .tor (errors if no node matched) or a <new node=spec> as handled by the global $()
objectthat passes is$() Assumes this ready-made DOM or jQuery node.
objectother Creates a new DOM node with these HTML attributes and calls el.data('sqimitive', this). Special keys: tag (defaults to div) and className (overrides class unless falsy to work around the reserved word).

data() allows you to reverse-lookup a Sqimitive instance from its DOM node. However, not only this is a generally deplorable practice but also not supported by some builds of Zepto.

In any case, el after init() is either null or a jQuery node with a non-zero length.

el is not automatically attached anywhere after construction, nor are its elEvents bound – call attach() for this. Base.render() also calls attach() but it does nothing if the attachPath _opt’ion is not set.

This property is not advised to change directly after init() but if you do then only set it to an is$() object.

See Views overview (vw) for the high-level idea.

Defined in: sqimitive-jquery.js, line 125

elEvents

from baseElEvents

from setOnDecl

May only be set upon declaration via Core::extend() or mixIn.

Declares event listeners for el, bound automatically by attach().

Value Types
Types Notes
object {event: func}

elEvents’ format is consistent among Base’s subclasses and follows Backbone’s bb:Model-events: keys are event references of form event[.ns][ .sel #ector] and values are strings (expandFunc()) or functions, called as function (nativeEventObject) (see argDanger).

from inMergeProps

This property is listed in _mergeProps by default so subclasses defining it add to their parents’ objects instead of overwriting them entirely (but identical keys are still overwritten).

The .ns part is ignored but can be used to create unique keys for the purpose of inheritance. By convention, the class’ own handlers don’t have ns while mixIn’s do.

from es6thiswarn

Warning: avoid using ES6 arrow functions as handlers due to their fixed this (es6this).

This property is not advised to change on run-time since it’s usually unpredictable when it will take effect. For example, Sqimitive\jQuery.attach() only binds events when nesting el into a new parent node:

sqim.attach()
sqim.elEvents['click'] = function () { alert('foo') }
sqim.attach()    // will do nothing and 'click' will not be bound
sqim.el.remove()
sqim.attach()    // now events are bound

See also the attachPath _opt.

Example
var MyView = Sqimitive.jQuery.extend({
  el: {tag: 'form'},

  elEvents: {
    // Attach listener to this object's el:
    submit: function (e) {
      return false
        // as with regular jQuery event handlers, returning false
        // implies e.stopPropagation() and e.preventDefault()
    },

    // React on change originating from an element with specific name
    // attribute:
    'change [name="login"]': function () {
      this.$('[name="password"]').val('')
    },

    // Call render() whenever value of an element with the name
    // attribute changes:
    'change [name]': 'render',

    // Masked callback - only gives first argument to _linkClicked():
    'click a': '_linkClicked.',

    // Similar but the '.zyx' namespace creates a second handler,
    // avoiding key collision with 'click a':
    'click.zyx a': 'track',
  },
})

var MyView2 = MyView.extend({
  elEvents: {
    // Overrides the '_linkClicked.' handler but keeps 'track'.
    'click a': ...,
  },
})

Defined in: sqimitive-jquery.js, line 188

Methods

is$ ( obj )

Modifiers: static

Determines if obj is a $ node (jq:jQuery or Zepto).

Sqimitive.jQuery.is$(document.rootElement)   //=> false
Sqimitive.jQuery.is$($('html'))    //=> true
Sqimitive.jQuery.is$($('<p>'))     //=> true
Sqimitive.jQuery.is$($())          //=> true
Sqimitive.jQuery.is$(null)         //=> false

Defined in: sqimitive-jquery.js, lines 405-407 (3 lines) • Show code

  is$: function (obj) {
    return obj instanceof $ || ($.zepto && $.zepto.isZ(obj))
  },
})
$ ( path )

Finds node(s) within own el.

Result Types
Types Notes
object A jq:jQuery node.

Possible path’s:

Arguments
Name Types Notes
objectthat is is$() to return the path itself Returned collection may be empty or its members may be outside of this.el
a DOM node to wrap and return $(path)
stringempty or just . to return own el If this.el is unset then always returns a jQuery node with zero length
selector to return this.el.find(path) – see jq:find()
Example
this.$()                //=> $(this.el) - but better use this.el
this.$('')              //=> $(this.el)
this.$('.')             //=> $(this.el)
this.$('a[href]')       //=> $([A, A, ...])
this.$(document.body)   //=> $('body')
this.$($('body'))       // same
this.$('body')          // empty collection unless this.el is <html>

this.el = null
this.$('')              //=> $()
this.$('body')          //=> $()
this.$(document.body)   //=> $('body')

Defined in: sqimitive-jquery.js, lines 373-381 (9 lines) • Show code

  $: function (path) {
    if (this.constructor.is$(path) || _.isElement(path)) {
      return $(path)
    } else if (this.el) {
      return (path == '' || path == '.') ? this.el : this.el.find(path)
    } else {
      return $()
    }
  },
}, {
attach ( parent )

Appends el to a DOM node and binds elEvents if changed parents.

Arguments
Name Types Notes
parentstring selector
object DOM or jQuery node
no argument use the attachPath _opt

attach() adds el using jq:appendTo() to parent resolved with _parent’s $() or global $() (if no _parent), calls attach() on all children of self (to let them rebind their DOM listeners – but doesn’t render() them) and, unless el is null, clears all existing el event listeners and binds those defined in elEvents under the .sqim-CID jQuery namespace (ignoring .ns possibly present in elEvents keys – that one is for key collision avoidance during inheritance, not for jQuery).

Example
sqim.attach('#nav')   //= sqim.el.appendTo('#nav')
sqim.attach('<div>')  //= appends to a new <div> node
sqim.attach('#nothinghereever!')      // does nothing
sqim.attach(null)                     // does nothing
sqim.attach()         // uses sqim._opt.attachPath, if set
sqim.attach(sqim.get('attachPath'))   // the same

Note: if _parent is set then its $() is used meaning that even if parent is a globally-reachable selector (like html,head,body) it will never match if _parent’s el is unset or if it’s not a parent of that node (to match head el must be $('html')). Work around this by setting attachPath to a DOM node:

el: document.rootElement,    // for <html>
el: document.head,           // for <head>
el: document.body,           // for <body>
  // warning: body is not available if script is executing from
  // <head>

attach() does nothing if no parent was found (el is not un-attached) or if this.el was already a direct child of that node (i.e. found the same parent) so performance penalty of subsequent attach() calls is small.

from baseAttach

Result Types
Types Notes
object this

Here’s a typical Sqimitive object lifecycle:

  • construct with new: new Class({opt...})
  • attach() (to DOM, etc.), for members – when nested to a collection
  • render() when required for user to see it first time
  • render() again when something changes that affects the visual presentation (usually in response to a change of some _opt’ion)
  • finally, remove()

Complete re-rendering on change is simple and adequate for many simple sqimitives but if it’s heavy, it’s customary to perform a partial update using method(s) named update(). There are no such methods by default but see vw for an example.

Example
var Label = Sqimitive.jQuery.extend({
  _opt: {caption: 'untitled'},

  events: {
    render: function () {
      this.el.text(this.get('caption'))
    },

    change_caption: 'render',
  },
})
                                    // Lifecycle:
;(new Label)                        // 1) construct
  .attach('body')                   // 2) attach
  .render()                         // 3) render
  .set('caption', 'render again!')  // 4) update

Defined in: sqimitive-jquery.js, lines 297-322 (26 lines) • Show code

attach: function (parent) {
  arguments.length || (parent = this.get('attachPath'))

  if (parent) {
    parent = this._parent ? this._parent.$(parent) : $(parent)
  }

  if ((parent || {}).length && parent[0] !== this.el[0].parentNode) {
    parent.append(this.el)
    // Notifying children of the "mount" node change to rebind their DOM
    // listeners.
    this.invoke('attach')
  }

  if (this.el) {
    var namespace = '.sqim-' + this._cid
    this.el.off(namespace)

    _.each(this.elEvents, function (func, key) {
      func = Sqimitive.Core.expandFunc(func, this)
      key = key.match(/^\s*([^\s.]+)(\.\S*)?(\s+(.*))?$/)
      key[1] += namespace
      key[4] ? this.el.on(key[1], key[4], func) : this.el.on(key[1], func)
    }, this)
  }
},
remove ( )

Removes own el from DOM and unnest()s this from _parent.

remove() calls jq:remove() on el (if set) to drop it from DOM, then the inherited Base.remove() calls unnest() to remove this sqimitive from its _parent (if any).

remove() doesn’t recursively remove() all nested _children as it might be undesired and slow (in DOM removing the parent node automatically unbinds events of all children). If children do need to do some clean-up actions when their parent is removed – do this.sink('remove') (sink() is recursive) or let them subscribe to _parent’s remove when owned.

from baseRemove

Result Types
Types Notes
object this

rmvsunnUse unnest() when an object is temporary elided from the hierarchy (e.g. because it’s changing parents). Use remove() when it’s completely destroyed and its “view” (DOM node, etc.) is no longer needed. By convention, remove() is also used on el-less objects (where it’s identical to unnest() in effect) to convey the intention of destroying them.

Example
var child = new Sqimitive.Base({el: '<p>Some text.</p>'})
var parent = new Sqimitive.Base({el: '<article>'})
parent.nest(child).attach(parent.el)
  // now we have <article><p>Some text.</p></article>

child.unnest()
  // we still have <article><p>Some text.</p></article> even though
  // child is no more listed under parent._children

// But if we would have done this:
child.remove()
  // we would have <article></article> with the child removed from
  // both the parent's _children list and from its the parent's el

Defined in: sqimitive-jquery.js, line 324