Class in Sqimitive jQuery

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

Defined in: jquery.js, line 59

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.

Sqimitive\jQuery\MixIn can be used to add this functionality into a sqimitive whose superclass is different from Base, possibly even on run-time.

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: jquery.js, line 132Show code

$: $,
_invokeAttach

Modifiers: protected

from settable

May be set upon declaration via Core::extend() or mixIn and/or read/written directly on run-time.

Specifies if attach() should call attach() on _children when it changed the parent of el.

According to default Sqimitive’s lifecycle (see render), jQuery’s attach() propagates recursively while render() happens only when explicitly called. This is because attach() is fairly fast and often nearly no-op while render() may repopulate the entire el.

If your application’s lifecycle is different, use _invokeAttach to disable the inherited attach() propagation without overriding =attach entirely.

Defined in: jquery.js, line 259Show code

_invokeAttach: true,
_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).

elfalse 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.
string
object

Defined in: jquery.js, line 182Show code

_opt: {},
_removeEl

Modifiers: protected

from settable

May be set upon declaration via Core::extend() or mixIn and/or read/written directly on run-time.

Specifies if el should be jq:remove()’d from DOM when this sqimitive is remove()’d.

If disabled, remove() only unbinds elEvents and other listeners from el but doesn’t notify _children so their nodes and listeners remain unchanged, nor does it empty() el’s contents produced by render() or children.

Use _removeEl when you need special behaviour in remove() but don’t want to override it entirely (=remove) in order to keep jQuery’s behaviour.

Defined in: jquery.js, line 274Show code

_removeEl: true,
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$() or canWrap() 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: jquery.js, line 184

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 subclass defining it adds to its parent’s object instead of overwriting it 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 handlers of 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: jquery.js, line 277

Methods

canWrap ( obj )

Modifiers: static

Determines if obj should be wrapped in $ for storing as el.

Returns true if obj is a native DOM Element, Document or Window. Returns false if other, including is$().

Sqimitive.jQuery.canWrap(document.head)  //=> true
Sqimitive.jQuery.canWrap(null)           //=> false
Sqimitive.jQuery.is$($('<p>'))           //=> true
Sqimitive.jQuery.canWrap($('<p>'))       //=> false
_.isElement(window)                      //=> false
Sqimitive.jQuery.canWrap(window)         //=> true
Sqimitive.jQuery.canWrap(document)       //=> true

Defined in: jquery.js, lines 161-163 (3 lines) • Show code

canWrap: function (obj) {
  return obj && (_.isElement(obj) || obj.nodeType === 9 || obj.window === obj)
},
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: jquery.js, lines 143-145 (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 (canWrap()) 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: jquery.js, lines 478-486 (9 lines) • Show code

$: function (path) {
  if (this.constructor.is$(path) || this.constructor.canWrap(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
null only bind events

First, unless parent is null, attach() resolves parent with _parent’s $() or global $() (if no _parent) and, if the found parent is a node and el’s current direct parent is different, calls jq:appendTo() on own el and attach() on all children of self to let them rebind their DOM listeners (but see _invokeAttach). Doesn’t un-attach el if no parent was found. Doesn’t call render().

Second, attach() 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 to avoid key collisions during inheritance, not for jQuery). This happens even if parent didn’t change (above).

If el is null then attach() does nothing.

Example
sqim.attach('#nav')   //= sqim.el.appendTo('#nav')
sqim.attach('<div>')  //= appends to a new <div> node
sqim.attach(null)     // re-binds elEvents only
sqim.attach('#unk')   // the same
sqim.attach()         // uses sqim._opt.attachPath, if set
sqim.attach(sqim.get('attachPath'))   // the same

Note: if _parent is set then attach() uses its $(). Hence 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. For example, el must be $('html') in order to match head:

var parent = new Sqimitive.jQuery({el: 'p'})
var child = parent.nest(new Sqimitive.jQuery).attach('body')
  //=> child.el.parent() is []

Work around this by setting attachPath or el to a DOM or $ node or by giving one to el after construction:

 var MyView = Sqimitive.jQuery.extend({
   el: window,
   el: document.rootElement,    // for <html>
   el: document.head,           // for <head>
   el: document.body,           // for <body>
})

// Or, on run-time by class' user:
new Sqimitive.jQuery({attachPath: document.rootElement})
new Sqimitive.jQuery({el: window})  // binds elEvents only
sqim.attach(document)               // binds elEvents only

Warning: document.body is not available if the current script is executing from <head>.

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: jquery.js, lines 402-424 (23 lines) • Show code

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

  if (this.el) {
    parent = parent && (this._parent ? this._parent.$(parent) : $(parent)) || []

    if (_.isElement(parent[0]) && parent[0] !== this.el[0].parentNode) {
      this.el.appendTo(parent)
      // Notifying children of the "mount" node change.
      this._invokeAttach && this.invoke('attach')
    }

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

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

If el is set, remove() calls either jq:remove() to drop it from DOM (if _removeEl is set) or jq:off() to unbind all own events. 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 perform some clean-up actions when their parent is removed – call 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: jquery.js, line 426