The AutoPlayDom widget is a tool that I have been developing to perform step by step DOM actions. The primary purpose of this tool is for debugging, so that a developer can play through the classes/styles that are applied at various times throughout the life of a webpage. Then the developer can view the pages in their graded-browser support matrix and verify that the everything appears as expect. However, the tool is flexible enough to perform a serious of DOM manipulations, which have the effect of animating a page (although it does not leverage an animation library, yet…).
The project file is available at http://www.mattsnider.com/assets/js/widget/AutoPlayDom.js.
Todays article will cover the two main (and most complicated) public methods of AutoPlayDom: add and next. The
add function allows more steps to added to the
_data array, which holds the logic for each step of DOM manipulation. The
next function performs the required tasks found in each step.
Example 1: AutoPlayDom.add()
add: function() { // iterate on the arguments for (var i = 0; i < arguments.length; i+= 1) { var o = arguments[i]; if (_YL.isArray(o)) { _that.add.apply(_that, o); } else if (o && o.name && o.tasks && o.tasks.length) { // validate _data.push(o); } else { alert(attempting to add invalid object); } } },
The add method in Example 1 uses the ArgumentOrArray Pattern, where the developer can pass in the tasks as: a single task object, many task objects as parameters, an array of many task objects, or many arrays containing task objects and/or task objects as parameters. The task objects will be added by their argument position first, then there position in the array. So the following:
Example 2: using AutoPlayDom.add()
var apd = Core.Widget.AutoPlayDom(); apd.add(object1); apd.add([object2,object3],object4, [object5]);
will play out in this order: object1,
object2,
object3,
object4,
object5. Lastly, the
add method validates that the task object has set
name and
tasks is an array. This will allow the
next method to run properly for this task object. Task objects should be of the form shown in Example 3.
Example 3: Task Object
{name: The Name of This Task, tasks: [ {search: [ // an array of search objects {method:name of a method attach to YAHOO.util.Dom, params: [/*an array of parameters to pass into method*/]} // each subquent object will use the resulting node or nodes from the previous search as their root parameter ], actionMethod:name of a method attach to YAHOO.util.Dom, actionParams: [ // array of actions parameters to apply to the node, using this actionMethod [/* array of parameters to use with actionMethod */] ]}, {search: [ {method:getElementsByClassName, params: [testClass,form, document.body]}, {method:getElementsBy, params: [fieldset]} ], actionMethod:setStyle, actionParams: [ [height,100px], // usesetStyle to apply the height to the node [width,300px], // usesetStyle to apply the width to the node [z-index, 100] // usesetStyle to apply the zIndex to the node ]} ]}
The comments in Example 3 explain how to use each part of the task object. Once added, the task objects can be executed using the next
method.
Example 4: AutoPlayDom.next()
next: function() { // go to the next position if (_that.hasNext()) { _index += 1; var o = _data[_index]; // iterate on the tasks _array_walk(o.tasks, function(t) { var isValidSearch = t.search && t.search.length, isValidAction = t.actionMethod && t.actionParams && t.actionParams.length, nodes = null; if (isValidSearch && isValidAction) { // iterate on the search methods _array_walk(t.search, function(o) { if (nodes) { var new_nodes = []; // iterate on the nodes and apply the search method _array_walk(nodes, function(node) { var fx = _YL.isFunction(o.method) ? o.method : _YD[o.method]; if (fx) { var new_params = []; if (o.params) { new_params = _YL.isArray(o.params) ? o.params : [o.params]; // normalize params } // various need the node placed in differing parameter locations if (_YL.isString(o.method)) { switch (o.method) { casegetElementsByClassName: // these may need to be updated, anytime listOfMethods in loadAutoPlayHelperFunctions is changed casegetElementsByTagName: // this group of functions puts the node at the end casegetElementsBy: new_params.push(node); break; default: // this group of functions puts the node at the beginning new_params = [node].concat(new_params); break; } } else { new_params = [node].concat(new_params); } var rs = fx.apply(window, new_params); if (rs) { if (_YL.isArray(rs)) { new_nodes = new_nodes.concat(rs); } else { new_nodes.push(rs); } } else { // this index will be removed } } else { throw(EXCEPTION: AutoPlayDom.Next - Invalid method used (+ o.method +); no elements found); } }); nodes = new_nodes; } else { var fx = _YL.isFunction(o.method) ? o.method : _YD[o.method]; nodes = fx.apply(window, _YL.isArray(o.params) ? o.params : [o.params]); if (! _YL.isArray(nodes)) {nodes = [nodes];} // convert to an array so we can do same logic, when array or not array } }); var actionMethod = _YL.isFunction(t.actionMethod) ? t.actionMethod : _YD[t.actionMethod]; if (actionMethod) { // iterate on the nodes and apply the action method _array_walk(nodes, function(node) { if (! _YL.isArray(t.actionParams)) {t.actionParams = [t.actionParams];} // convert to an array so we can do same logic, when array or not array _array_walk(t.actionParams, function(params) { actionMethod.apply(window, [node].concat(params)); }); }); } else { throw(EXCEPTION: AutoPlayDom.Next - Invalid actionMethod used (+ t.actionMethod +); method not found on YAHOO.util.Dom); } } else { throw(EXCEPTION: AutoPlayDom.Next - Invalid task used (isValidSearch:+ isValidSearch +, isValidAction:+ isValidAction +)); } }); } },
The next
method in Example 3 moves the DOM state from the current task object to the next. The next
method first validates that there is another task object available, then walks through each task on the task object. These tasks are checked if they contain a search
array, an actionMethod
, and an array of parameter arrays (actionParams
). Next each object is search
is iterated on. Each search
object should contain a method
and an array of parameters (params
). If there is only one search
object, then the method moves forwarding using the node(s) found. However, when there are more than one search
objects, the method iterates through nodes, calling the new search
methods using the previously found node(s) as a the root node. Any method found this way will replace the values previously in the nodes
list. There is some special-case logic for various methods where the root node is the last parameter, instead of the first parameter, but the default is to use the previous node as the first parameter, when calling the search
method.
Once all the nodes
are found and the actionMethod
is defined, the nodes are iterated on for DOM manipulation. The actionMethod
can be found on "YAHOO.util.Dom" by default (such as setStyle
or addClass
) or a function of the developers own creation. It is assumed for these methods that the node
should be the first parameter. Lastly, for each node
, the actionParam
array is iterated on, to call the actionMethod
with the node
and each actionParam
.
This tool is versioned 0.5
, because I intend to write a previous
function which will be a dynamic way to iterate backwards through DOM actions that were previously executed in the forward direction. This is going to be very tricky and I did not have time for it this week. When I finish the previous method I will create a test page so that you can experiment with AutoPlayDom.