Auto-instantiates widgets/classes based on parsed, declarative HTML.
new Behavior([options]);
true to NOT catch these errors to allow them to be handled by the browser.document.body;[data-behavior]. This can also be a function which, when executed, returns the elements as an Elements collection; it is passed the container option if present. Important If you use a different data- property than behavior you need to also change Behavior.elementDataProperty to match. This property defaults to behavior, meaning that elements have a property defined for data-behavior. If you wanted to use data-be for example, you would need to set the selector option here to [data-be] AND include Behavior.elementDataProperty = "be"; in your code.Behavior is applied to an element whenever you want to parse that element's DOM for filters declared in the HTML of that element. Behavior then finds all elements with a "data-behavior" property defined, invoking the filters named there. It will only invoke a filter once, so it's safe to run it more than once (if the DOM changes, for example).
var myBehavior = new Behavior();
myBehavior.apply(myContainerElement);
<div data-behavior="Accordion">
<div class="toggle">Toggle 1</div>
<div class="target">This area is controlled by Toggle 1.</div>
<div class="toggle">Toggle 2</div>
<div class="target">This area is controlled by Toggle 2.</div>
</div>
The above example will invoke the registered "Accordion" filter. See the section on Behavior.Filter below.
Behavior uses a clearly defined API to read HTML properties off the elements it configures. See BehaviorAPI for details as well as passMethod for methods that Behavior instances themselves provide.
It's possible to declare more than one data filter property for a single element (data-behavior="FormRequest FormValidator")
When a filter performs an action that other filters might find useful to know about, the preferred usage is to fire an event on your Behavior instance via the behaviorAPI object passed to your filter. This provides a non-brittle way for filters to react to each other. For example, a Delegator that fetches new content via AJAX should fire the 'ammendDom' event. This event is also fired on the Behavior instances it's bound to (if it is). These two events are prescribed here, but you can use any you find useful. This allows filters to avoid referencing themselves.
cleanup method if you set one in the options. Passed the element destroyed as an argument.apply method if you set one in the options. Passed two arguments: the parent node that contains all the updated elements and an array of those elements updated.Other events the author has used is to denote layout changes, layout:changed, layout:display, layout:size, etc.
Defines a method that will be passed to filters. Behavior allows you to create a well defined API for filters to reference which increases their reusability. You define this API by explicitly passing named functions to them through the Behavior instance.
myBehaviorInstance.passMethod(name, function);
By default, Behavior passes the following methods to filters in addition to the methods defined in the BehaviorAPI
addEvent method of the behavior instance provided by the Events class.removeEvent method of the behavior instance provided by the Events class.addEvents method of the behavior instance provided by the Events class.removeEvents method of the behavior instance provided by the Events class.fireEvents method of the behavior instance provided by the Events class.apply method of the behavior instance. This allows a Behavior to create new DOM structures and apply their behavior filters.applyFilter method of the behavior instance. Allows you to invoke a specific behavior filter.document.body. Set options.container to change it.getContentElement().getSize(); Note that if that element is not in the DOM this will return zeros.setDelegator method.error event with the arguments passed.You can add any other methods that your filters require. In general, your filters shouldn't reference anything in your environment except these methods and those methods defined in Behavior.Filter.
Iterates over an object of key/values passing them to the passMethod method.
myBehaviorInstance.passMethods(obj);
Applies all the behavior filters for an element and its children.
myBehaviorInstance.apply(container[, force]);
true elements that have already been processed will be processed again.Applies a specific behavior filter to a specific element (but not its children).
myBehaviorInstance.applyFilter(element, filter, [, force]);
true elements that have already been processed will be processed again.Given a name, return the registered filter.
myBehaviorInstance.getFilter(name);
undefined if one is not found.Given a name, return the plugins registered for a filter by that name. See the section on Behavior.Filter for details.
myBehaviorInstance.getPlugins(name);
undefined if one is not found.Garbage collects the specified element, cleaning up all the filters applied to it. This should be invoked when you delete an element from the DOM. When you cleanup an element its children are also garbage collected.
myBehaviorInstance.cleanup(element);
Behavior applies all the registered filters to the element you specify (and its children). This requires that each element that should have a behavior applied name the filters it needs in its data-behavior property. It also means that every named filter must be registered.
Filters can be registered to an instance of Behavior or to the global Behavior namespace. So if you register a "Foo" filter globally, all instance of Behavior get that filter. If a specific instance of Behavior defines a "Foo" filter, then the local instance is used regardless of the presence of a global filter.
There are methods to add and remove filters to instances as well as to the global namespace.
Add a new filter.
myBehaviorInstance.addFilter(name, filter[, overwrite]);
true and there is already an existing filter by the given name, that filter will be replaced, otherwise the original is retained.Adds a group of filters.
myBehaviorInstance.addFilters(obj[, overwrite]);
true and there is already an existing filter by the given name, that filter will be replaced, otherwise the original is retained.Add a new plugin for a specified filter.
myBehaviorInstance.addPlugin(filterName, pluginName, fn[, overwrite]);
true and there is already an existing plugin by the given name, that plugin will be replaced, otherwise the original is retained.Adds a group of plugins.
myBehaviorInstance.addPlugins(obj[, overwrite]);
true and there is already an existing plugin by the given name, that filter will be replaced, otherwise the original is retained.Sets the default values for a filter, overriding any defaults previously defined.
myBehaviorInstance.setFilterDefaults(name, defaults);
Stores a reference to a [Delegator][] instance that is returned by api.getDelegator().
myBehavior.setDelegator(myDelegator);
Returns a reference to the [Delegator][] instance that was set with setDelegator.
myBehavior.getDelegator();
setDelegator.Add a new filter to the global Behavior namespace.
myBehaviorInstance.addGlobalFilter(name, fn[, overwrite]);
Adds a group of filters to the global Behavior namespace.
myBehaviorInstance.addGlobalFilters(obj[, overwrite]);
Add a new global plugin for a specified filter.
myBehaviorInstance.addGlobalPlugin(filterName, pluginName, fn[, overwrite]);
Adds a group of plugins to the global Behavior namespace.
myBehaviorInstance.addGlobalPlugins(obj[, overwrite]);
Given a name, return the registered global filter.
Behavior.getFilter(name);
undefined if one is not found.Sets the default values for a filter, overriding any defaults previously defined.
Behavior.setFilterDefaults(name, defaults);
Behavior Filters are where you define what to do with elements that are marked with that filter. Elements can have numerous filters defined and filters can do anything with those elements that they like. In general, filters should only alter the element given, though it is possible to have elements that relate to others (for example, an Accoridon filter might set up an instance of Fx.Accordion using children that are the togglers and sections).
Typically filters allow for configuration using HTML5 data- properties, classes, and element attributes. See the BehaviorAPI which automates the reading of these properties.
An important rule of filters is that they cannot know about each other or be in any way dependent on each other. When two filters need to be managed differently when both are present, use a plugin (this should be rare).
Filters are typically not created with the constructor (i.e. new Behavior.Filter) but instead with the addFilter/addFilters methods defined on the Behavior instance or the addGlobalFilter/addGlobalFilters methods on the Behavior namespace.
Filters nearly always return instances of classes (this is essentially their purpose). It's not a requirement, but it is generally preferred.
Behavior.addGlobalFilters({
Accordion: function(element, api) {
var togglers = element.getElements(api.get('togglers'));
var sections = element.getElements(api.get('sections'));
if (togglers.length == 0 || sections.length == 0) api.fail('There are no togglers or sections for this accordion.');
if (togglers.length != sections.length) api.warn('There is a mismatch in the number of togglers and sections for this accordion.')
var accordion = new Fx.Accordion(togglers, sections);
api.onCleanup(function() {
accordion.detach();
});
return accorion; //note that the instance is always returned!
}
});
/* the matching HTML
<div data-behavior="Accordion" data-Accordion-togglers=".toggle" data-Accordion-sections=".section">
<div class="toggle">Toggle 1</div>
<div class="target">This area is controlled by Toggle 1.</div>
</div> */
In the example above our filter finds the sections and togglers and validates that there is at least one of each. If there aren't it calls api.fail - this stops the filter's execution and Behavior.js catches it and calls its onError event (which defaults to console.error). It also checks if the number of togglers and the number of sections are equal and calls api.warn if they are off. This does not top execution; it only fires the onWarn event on Behavior (which defaults to console.warn).
A simple filter is just a function and a name ("Accordion") and the function that creates accordions given an element and the api object. This is fine, but it's possible to express more complex configurations. Example:
Behavior.addGlobalFilters({
Accordion: {
//if your filter does not return an instance of this value Behavior will throw an error
//which is caught and logged by default
returns: Accordion,
require: ['togglers', 'togglers'],
//or
requireAs: {
togglers: String,
someNumericalValue: Number,
someArrayValue: Array
},
//you wouldn't define defaults for required values, but this is just an example
defaults: {
togglers: '.toggler',
sections: '.sections',
initialDisplayFx: false
},
//simple example:
setup: function(element, API){
var togglers = element.getElements(api.get('togglers'));
var sections = element.getElements(api.get('sections'));
if (togglers.length == 0 || sections.length == 0) api.fail('There are no togglers or sections for this accordion.');
if (togglers.length != sections.length) api.warn('There is a mismatch in the number of togglers and sections for this accordion.')
var accordion = new Fx.Accordion(togglers, sections,
api.getAs({
fixedHeight: Number,
fixedWidth: Number,
display: Number,
show: Number,
height: Boolean,
width: Boolean,
opacity: Boolean,
alwaysHide: Boolean,
trigger: String,
initialDisplayFx: Boolean,
returnHeightToAuto: Boolean
})
);
api.onCleanup(function() {
accordion.detach();
});
return accorion; //note that the instance is always returned!
},
//don't instantiate this value until the user mouses over the target element
delayUntil: 'mouseover,focus',
//OR delay for a specific period
delay: 100,
//OR let me initialize the function manually
initializer: function(element, API){
element.addEvent('mouseover', API.runSetup); //same as specifying event
//or
API.runSetup.delay(100); //same as specifying delay
//or something completely esoteric
var timer = (function(){
if (element.hasClass('foo')){
clearInterval(timer);
API.runSetup();
}
}).periodical(100);
//or
API.addEvent('someBehaviorEvent', API.runSetup);
});
}
});
In the long-form example above, we see that filters can be passed as objects that map to the config option in the Behavior.Filter constructor arguments. (see Behavior.Filter's constructor) below.
Behavior has a way to define API methods passed to filters for their use. To use these methods, access them in the second argument passed to your filter function:
Behavior.addGlobalPlugins({
MeasureOnResize: function(element, api) {
api.addEvent('resize', updater);
api.onCleanup(function(){
api.removeEvent('resize', updater);
});
}
});
var myBehaviorInstance = new Behavior();
myBehaviorInstance.apply(document.body); //applies all filters named in your content
//let's assume there's an element with the data-behavior property set to MeasureOnResize
myBehaviorInstance.fireEvent('resize');
As you can see in the example above, we add an event whenever the Behavior instance fires a "resize" method. We also clean up that event with the markForCleanup method which is passed through the api object as "onCleanup".
While is common (and recommended) for filters to be declared using Behavior's addFilter method it's possible to create a filter on its own.
new Behavior.Filter(name, filter);
this.name on the instance of the filter..setup property on the object) expect to be invoked with an element and an instance of BehaviorAPI passed to it. Filters in general expect this API object to be provided by a Behavior instance which also adds additional methods (see Behavior.passMethod) for more details.If the second argument passed to the constructor is an object, the following options are specified:
mouseover,focus). Whichever happens first will invoke the filter.api.runSetup, which this initializer can invoke when it pleases (or not at all).You can namespace your filters to avoid conflicts. Simply give your filter a name with dots in it (data-behavior="Foo.Bar") and then reference any arguments with dashes (data-foo-bar-options="...").
Adds a function to invoke when the element referenced is cleaned up by the Behavior instance. Note that Behavior passes this method through as "onCleanup" in it's API object.
myBehaviorFilter.markForCleanup(element, fn);
//more commonly inside a filter:
api.onCleanup(fn); //element is not specified on the api object
Garbage collects the specific filter instance for a given element. This is typically handled by the Behavior instance when you call its cleanup method.
myBehaviorFilter.cleanup(element);
//more commonly
myBehaviorInstance.cleanup(container);
//here the container can be any element that is being removed from the DOM
//all its children that have had filters applied will have their cleanup method run
Filter Plugins are identical to regular filters with the exception that they are invoked only when the filter they are altering is invoked and always after that. Filters do not have any guarantee that they will be invoked in any given order, but plugins are always guaranteed to be invoked after the filter they reference. More specifically, they are always invoked after all the filters on an element are invoked. If an element has two filters (A and B) and each of these filters have plugins (A1 and B1) the invocation order will be A, B, A1, B1.
Behavior.addFilter('Mask', function(element, api){
var maskInstance = new Mask(element);
//this is silly
var events = {
mouseover: maskInstance.show.bind(maskInstance),
mouseout: maskInstance.hide.bind(maskInstance)
};
element.addEvents(events);
api.onCleanup(function(){
element.removeEvents(events);
});
return maskInstance; //note that we return the instance!
});
Behavior.defineGlobalPlugin('Mask', 'AlertOnMask', function(element, api, maskInstance){
//also silly
var aleter = function(){ alert('the mask is visible!'); };
maskInstance.addEvent('show', alerter);
api.onCleanup(function(){
maskInstance.removeEvent('show', alerter);
});
});
The above example is guaranteed to always run after the "Mask" filter. You can define a plugin for a plugin just as well; simply name the plugin as the first argument (you could create a plugin for the above example by making a plugin for "AlertOnMask"). Plugin setup functions are passed not only the target element and the api object but also the instance returned by the filter they augment.
Behavior implements the following helper methods on the Element prototype.
Adds a data filter to the element.
myElement.addBehaviorFilter(name);
Removes a data filter to the element.
myElement.removeBehaviorFilter(name);
Gets an array of data filters specified on an element.
myElement.getBehaviors();
Returns true if the element has the specified data filter.
myElement.hasBehavior(name);
true if the element has the specified data filter.Filters generally return the instance of the widget they instantiate. This method allows you to access that widget.
myElement.getBehaviorResult(name);