From 6a7b2fa7d731240a4f59a483fee01c73c758e648 Mon Sep 17 00:00:00 2001 From: rolux Date: Thu, 21 Aug 2014 17:23:33 +0200 Subject: [PATCH] Make Ox.$ work with more than one element --- source/Ox/js/DOM.js | 711 ++++++++++++++++++++++++++++---------------- 1 file changed, 447 insertions(+), 264 deletions(-) diff --git a/source/Ox/js/DOM.js b/source/Ox/js/DOM.js index 42cadfe2..59490b1b 100644 --- a/source/Ox/js/DOM.js +++ b/source/Ox/js/DOM.js @@ -30,11 +30,10 @@ Ox.$ Generic HTML element, mimics jQuery Ox.$ = Ox.element = function(value) { var data = {}, - element = !Ox.isString(value) ? value // window, document or element - : value[0] == '<' ? document.createElement(value.slice(1, -1)) - : value[0] == '#' ? document.getElementById(value.slice(1)) - : value[0] == '.' ? document.getElementsByClassName(value.slice(1))[0] - : document.getElementsByTagName(value)[0], + elements = Ox.isArray(value) ? value // array of elements + : !Ox.isString(value) ? [value] // window, document or element + : value[0] == '<' ? [document.createElement(value.slice(1, -1))] + : Ox.slice(document.querySelectorAll(value)), mousewheelEvents = ['wheel', 'mousewheel'], originalMousewheelEvents = 'onwheel' in document ? ['wheel'] : ['mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll']; @@ -53,259 +52,369 @@ Ox.$ = Ox.element = function(value) { return ret; } - return element ? { - //@ 0 The DOM element itself - 0: element, + function removeOxElements(parent, includeParent) { + if (includeParent) { + removeOxElement(parent); + } else { + parent.find('.OxElement').forEach(removeOxElement); + } + function removeOxElement(element) { + Ox.getOxElement(element).removeElement(); + } + } + + return elements.length ? Ox.extend( + Ox.zipObject(Ox.range(elements.length), elements + ), { /*@ - addClass Adds a class name - (className) -> This element - className Class name + add Adds another DOM object to this DOM object + (other) -> This DOM object + other Other DOM object @*/ - addClass: function(string) { - this[0].className = Ox.unique((( - this[0].className ? this[0].className + ' ' : '' - ) + Ox.clean(string)).split(' ')).join(' '); + add: function add($other) { + elements = Ox.unique(elements.concat(other.elements())); + this.length = elements.length; return this; }, /*@ - append Appends one or more elements to this element - (element[, element[, ...]]) -> This element - element Another element + addClass Adds a class name to all elements + (className) -> This DOM object + className Class name @*/ - append: function() { - var that = this; - Ox.slice(arguments).forEach(function($element) { - that[0].appendChild($element[0]); + addClass: function addClass(string) { + string = Ox.clean(string); + elements.forEach(function(element) { + element.className = Ox.unique((( + element.className ? element.className + ' ' : '' + ) + string).split(' ')).join(' '); }); return this; }, /*@ - appendTo Appends this element object to another element object - (element) -> This element - element Another element + append Appends one or more DOM objects to this DOM object + (object[, object[, ...]]) -> This DOM object + element Another DOM object @*/ - appendTo: function($element) { - $element[0].appendChild(this[0]); + append: function append() { + var $others = Ox.slice(arguments); + elements.forEach(function(element) { + $others.forEach(function($other) { + $other.forEach(function(otherElement) { + element.appendChild(otherElement); + }); + }); + }); return this; }, /*@ - attr Gets or sets an attribute + appendTo Appends this DOM object to another DOM object + (object) -> This DOM object + object Another DOM object + @*/ + appendTo: function appendTo($other) { + $other.forEach(function(otherElement) { + elements.forEach(function(element) { + otherElement.appendChild(element); + }); + }); + return this; + }, + /*@ + attr Gets or sets an attribute for all elements (key) -> Value - (key, value) -> This element - ({key0: value0, key1: value1, ...}) -> This element + (key, value) -> This DOM object + ({key0: value0, key1: value1, ...}) -> This DOM object key Attribute name value Attribute value @*/ - attr: function() { - var ret, that = this; - if (arguments.length == 1 && Ox.isString(arguments[0])) { + attr: function attr() { + var args = arguments, ret, that = this; + if (args.length == 1 && Ox.isString(args[0])) { ret = this[0].getAttribute - ? this[0].getAttribute(arguments[0]) - : null; + ? this[0].getAttribute(args[0]) + : void 0; // fixme: why exactly is this needed? - if (ret === null) { - ret = void 0; - } + return ret === null ? void 0 : ret; } else { - Ox.forEach(Ox.makeObject(arguments), function(value, key) { - that[0].setAttribute && that[0].setAttribute(key, value); + args = Ox.makeObject(args); + elements.forEach(function(element) { + Ox.forEach(args, function(value, key) { + if (element.setAttribute) { + element.setAttribute(key, value); + } + }); }); - ret = this; + return this; } - return ret; }, /*@ - children Returns the children of this element + children Returns the unique list of children of all elements () -> <[h]> Children @*/ - children: function() { - return Ox.slice(this[0].childNodes); + children: function children() { + return Ox.unique(Ox.flatten(elements.map(function(element) { + return Ox.slice(element.childNodes); + }))); }, /*@ - css Gets or sets a CSS attribute + css Gets or sets a CSS attribute for all elements (key) -> Value - (key, value) -> This element - ({key0: value0, key1: value1, ...}) -> This element + (key, value) -> This DOM object + ({key0: value0, key1: value1, ...}) -> This DOM object key Attribute name value Attribute value @*/ - css: function() { - var ret, that = this; - if (arguments.length == 1 && Ox.isString(arguments[0])) { - ret = this[0].style[arguments[0]]; + css: function css() { + var args = arguments; + if (args.length == 1 && Ox.isString(args[0])) { + return elements[0].style[args[0]]; } else { - Ox.forEach(Ox.makeObject(arguments), function(value, key) { - that[0].style[key] = value; + args = Ox.makeObject(args); + elements.forEach(function(element) { + Ox.forEach(args, function(value, key) { + element.style[key] = value; + }); }); - ret = this; + return this; } - return ret; }, /*@ data Gets or sets data () -> All data (key) -> Value - (key, value) -> This element - ({key0: value0, key1: value1, ...}) -> This element + (key, value) -> This DOM object + ({key0: value0, key1: value1, ...}) -> This DOM object key Property value <*> Value @*/ - data: function() { - var ret; + data: function data() { if (arguments.length == 0) { - ret = data; + return data; } else if (arguments.length == 1 && Ox.isString(arguments[0])) { - ret = data[arguments[0]]; + return data[arguments[0]]; } else { Ox.forEach(Ox.makeObject(arguments), function(value, key) { data[key] = value; }); - ret = this; + return this; } - return ret; }, /*@ - empty Empties the inner HTML - () -> This element + elements Returns all elements + () -> <[h]> All elements @*/ - empty: function() { + elements: function elements() { + return elements; + }, + /*@ + empty Empties the inner HTML of all elements + () -> This DOM object + @*/ + empty: function empty() { return this.html(''); }, /*@ - find Find descendant elements + every Tests if every element satisfies a given condition + (test) -> True if every element passes the test + test Test function + @*/ + every: function every() { + return Array.prototype.every.apply(elements, arguments); + }, + /*@ + filter Filters all elements by a given condition + (test) -> Array of matching elements + test Test function + @*/ + filter: function filter() { + return Array.prototype.filter.apply(elements, arguments); + }, + /*@ + find Find all descendant elements matching a CSS selector ([selector]) -> <[h]> Elements selector CSS selector @*/ - find: function(string) { - return Ox.slice(this[0].querySelectorAll(string || '*')); + find: function find(string) { + return Ox.unique(elements.map(function(element) { + return this[0].querySelectorAll(string || '*'); + })); + }, + /*@ + forEach Loops over all elements + (iterator) -> This DOM object + iterator Iterator function + @*/ + forEach: function forEach() { + Array.prototype.forEach.apply(elements, arguments); + return this; }, /*@ hasClass Returns true if this element has a given class (className) -> True if this element has the class className Class name @*/ - hasClass: function(string) { - return this[0].className.split(' ').indexOf(string) > -1; + hasClass: function hasClass(string) { + return elements.any(function(element) { + return Ox.contains(element.className.split(' '), string); + }); }, /*@ - height Returns the height of this element + height Returns the height of the first element () -> Height in px @*/ - height: function() { - return this[0].offsetHeight; + height: function height() { + return elements[0].offsetHeight; }, /*@ - hide Hides this element - () -> This element + hide Hides all elements + () -> This DOM object @*/ - hide: function() { + hide: function hide() { return this.css({display: 'none'}); }, /*@ - html Gets or sets the inner HTML + html Gets or sets the innerHTML of all elements () -> The inner HTML - (html) -> This element + (html) -> This DOM object html The inner HTML @*/ - html: function(string) { - var ret; + html: function html(string) { + var html = ''; if (arguments.length == 0) { - ret = this[0].innerHTML; + elements.forEach(function(element) { + html += element.innerHTML; + }) + return html; } else { - this[0].innerHTML = string; - ret = this; + elements.forEach(function(element) { + removeOxElements(element); + element.innerHTML = string; + }); + return this; } - return ret; }, /*@ - insertAfter Inserts this element after another element - (element) -> This element - element Another element + insertAfter Inserts this DOM object after another DOM object + (object) -> This DOM object + object Another DOM object @*/ - insertAfter: function($element) { - $element[0].parentNode.insertBefore(this[0], $element[0].nextSibling); + insertAfter: function insertAfter($other) { + var nextSibling = $element[0].nextSibling; + elements.forEach(function(element) { + $other[0].parentNode.insertBefore(element, nextSibling); + }) return this; }, /*@ - insertBefore Inserts this element before another element - (element) -> This element - element Another element + insertBefore Inserts this DOM object before another DOM object + (object) -> This DOM object + object Another DOM object @*/ - insertBefore: function($element) { - $element[0].parentNode.insertBefore(this[0], $element[0]); + insertBefore: function insertBefore($other) { + elements.forEach(function(element) { + $other[0].parentNode.insertBefore(element, $other[0]); + }); return this; }, /*@ - is Returns true if the element matches the query + is Tests if any element matches a CSS selector + (selector) -> True if the element matches the selector + selector CSS selector @*/ - is: function(query) { - return Ox.contains(this.parent().querySelectorAll(query), this[0]); + is: function is(string) { + return elements.some(function(element) { + return Ox.contains( + (element.parentNode || document).querySelectorAll(string), + element + ); + }); }, /*@ - next Returns the sibling after this element - () -> Next element + length Number of elements @*/ - next: function() { - return this[0].nextSibling; + length: elements.length, + /*@ + map Transforms all elements + (iterator) -> [] Transformed elements + iterator Iterator function + @*/ + map: function map() { + return Array.prototype.filter.map(elements, arguments); }, /*@ - nextAll Returns all siblings after this element - () -> <[h]> Next elements + next Returns the unique list of siblings directly after all elements + () -> <[h]> Siblings @*/ - nextAll: function() { - var sibling = this[0], siblings = []; - while (true) { - sibling = sibling.nextSibling; - if (!sibling) { - break; + next: function next() { + return Ox.unique(Ox.filter(elements.map(function(element) { + return element.nextSibling; + }))); + }, + /*@ + nextAll Returns the unique list of siblings after all elements + () -> <[h]> Siblings + @*/ + nextAll: function nextAll() { + var siblings = []; + elements.forEach(function(element) { + var sibling = element; + while (true) { + sibling = sibling.nextSibling; + if (!sibling) { + break; + } + siblings.push(sibling); } - siblings.push(sibling); - } - return siblings; + }); + return Ox.unique(siblings); }, /*@ off Unbinds a callback from an event - (event) -> This element (unbinds all callbacks) - (event, callback) -> This element - ({event0: callback0, event1: callback1, ...}) -> This element + (event) -> This DOM object (unbinds all callbacks) + (event, callback) -> This DOM object + ({event0: callback0, event1: callback1, ...}) -> This DOM object event Event name callback Callback function @*/ - off: function(event, callback) { - var that = this; - Ox.forEach(normalizeEvents(arguments), function(callback, event) { - if (callback) { - that[0].removeEventListener(event, callback, false); - } else { - that[0]['on' + event] = null; - } + off: function off(event, callback) { + var args = normalizeEvents(arguments); + elements.forEach(function(element) { + Ox.forEach(args, function(callback, event) { + if (callback) { + element.removeEventListener(event, callback, false); + } else { + element['on' + event] = null; + } + }); }); return this; }, /*@ on Binds a callback to an event - (event, callback) -> This element - ({event0: callback0, event1: callback1, ...}) -> This element + (event, callback) -> This DOM object + ({event0: callback0, event1: callback1, ...}) -> This DOM object event Event name callback Callback function e Event properties @*/ - on: function() { - var that = this; - Ox.forEach(normalizeEvents(arguments), function(callback, event) { - that[0].addEventListener(event, callback, false); + on: function on() { + var args = normalizeEvents(arguments); + elements.forEach(function(element) { + Ox.forEach(args, function(callback, event) { + element.addEventListener(event, callback, false); + }); }); return this; }, /*@ one Binds a callback to an event and unbinds it on first invocation - (event, callback) -> This element - ({event0: callback0, event1: callback1, ...}) -> This element + (event, callback) -> This DOM object + ({event0: callback0, event1: callback1, ...}) -> This DOM object event Event name callback Callback function e Event properties @*/ - one: function(events) { + one: function one(events) { var args = Ox.slice(arguments), that = this; Ox.forEach(normalizeEvents(arguments), function(callback, event) { that.on(event, function fn() { @@ -316,206 +425,280 @@ Ox.$ = Ox.element = function(value) { return this; }, /*@ - parent Returns the parent of this element - () -> Parent element + parent Returns the unique list of parents of all elements + () -> <[h]> Parent elements @*/ - parent: function() { - return this[0].parentNode; + parent: function parent() { + return Ox.unique(elements.map(function(element) { + return element.parentNode; + })); }, /*@ - parents Returns all ancestors of this element + parents Returns the unique list of all ancestors of all elements () -> <[h]> Ancestor elements @*/ - parents: function() { - var parent = this[0], parents = []; - while (true) { - parent = parent.parentNode; - if (!parent) { - break; + parents: function parents() { + var parents = []; + Ox.reverse(elements).forEach(function(element) { + var parent = element; + while (true) { + parent = parent.parentNode; + if (!parent) { + break; + } + parents.unshift(parent); } - parents.unshift(parent); - } - return parents; + }); + return Ox.unique(parents); }, /*@ - prepend Prepends one or more elements to this element - (element[, element[, ...]]) -> This element - element Another element + prepend Prepends one or more DOM objects to this DOM object + (object[, object[, ...]]) -> DOM object + object Another DOM objectt @*/ - prepend: function() { - var parent = this[0].parentNode; - Ox.slice(arguments).reverse().forEach(function($element) { - parent.insertBefore($element[0], parent.firstChild); + prepend: function prepend() { + var $others = Ox.slice(arguments).reverse(); + elements.forEach(function(element) { + var parent = element.parentNode; + $others.forEach(function($other) { + $other.forEach(function(otherElement) { + parent.insertBefore(otherElement, parent.firstChild); + }); + }); }); return this; }, /*@ - prependTo Prepends this element object to another element object - (element) -> This element - element Another element + prependTo Prepends this DOM object to another DOM object + (object) -> This DOM object + object Another DOM object @*/ - prependTo: function($element) { - var element = $element[0]; - element.insertBefore(this[0], element.firstChild); + prependTo: function prependTo($other) { + $other.forEach(function(otherElement) { + var firstChild = otherElement.firstChild + elements.forEach(function(element) { + otherElement.insertBefore(element, firstChild); + }); + }); return this; }, /*@ - prev Returns the sibling before this element - () -> Next element + prev Returns the unique list of siblings directly before all elements + () -> <[h]> Siblings @*/ - prev: function() { - return this[0].previousSibling; + prev: function prev() { + return Ox.unique(Ox.filter(elements.map(function(element) { + return element.previousSibling; + }))); }, /*@ - prevAll Returns all siblings before this element - () -> <[h]> Next elements + prevAll Returns the unique list of siblings before all elements + () -> <[h]> Siblings @*/ - prevAll: function() { - var sibling = this[0], siblings = []; - while (true) { - sibling = sibling.previousSibling; - if (!sibling) { - break; + prevAll: function prevAll() { + var siblings = []; + Ox.reverse(elements).forEach(function(element) { + var sibling = element; + while (true) { + sibling = sibling.previousSibling; + if (!sibling) { + break; + } + siblings.unshift(sibling); } - siblings.unshift(sibling); - } - return siblings; + }); + return Ox.unique(siblings); }, /*@ - remove Removes this element from the DOM - () -> This element + reduce Applies `reduce` to all elements @*/ - remove: function() { - this[0].parentNode.removeChild(this[0]); + reduce: function reduce() { + return Array.prototype.reduce.apply(elements, arguments); + }, + /*@ + remove Removes all element from the DOM + () -> This DOM object + @*/ + remove: function remove() { + elements.forEach(function(element) { + if (element.parentNode) { + removeOxElements(element, true); + element.parentNode.removeChild(element); + } + }); return this; }, /*@ - removeAttr Removes an attribute - (key) -> This element - ([key0, key1, ...]) -> This element + removeAttr Removes an attribute from all elements + (key) -> This DOM object + ([key0, key1, ...]) -> This DOM object key The attribute @*/ - removeAttr: function() { - var that = this; - Ox.makeArray(arguments[0]).forEach(function(key) { - that[0].removeAttribute(key); + removeAttr: function removeAttr() { + var keys = Ox.makeArray(arguments); + elements.forEach(function(element) { + keys.forEach(function(key) { + element.removeAttribute(key); + }); }); return this; }, /*@ - removeClass Removes a class name - (className) -> This element + removeClass Removes a class name from all elements + (className) -> This DOM object className Class name @*/ - removeClass: function(string) { - var array = Ox.clean(string).split(' '); - this[0].className = this[0].className.split(' ').filter( - function(className) { - return array.indexOf(className) == -1; + removeClass: function removeClass(string) { + var classNames = Ox.clean(string).split(' '); + elements.forEach(function(element) { + element.className = element.className.split(' ') + .filter(function(className) { + return !Ox.contains(classNames, className) + }) + .join(' '); + }); + return this; + }, + /*@ + replace Replaces another DOM object with this DOM object + (object) -> This DOM object + object Another DOM object + @*/ + replace: function replace($other) { + $other.forEach(function(otherElement) { + var parent = otherElement.parentNode, + sibling = otherElement.nextSibling; + if (parent) { + removeOxElements(otherElement, true); + parent.removeChild(otherElement); + elements.forEach(function(element) { + parent.insertBefore(element, sibling) + }); } - ).join(' '); + }); return this; }, /*@ - replace Replaces another element with this element - (element) -> This element - element Another element + replaceWith Replaces this DOM object with another DOM object + (object) -> This DOM object + object Another DOM object @*/ - replace: function($element) { - var next = $element[0].nextSibling, parent = $element[0].parentNode; - $element.remove(); - parent.insertBefore(this[0], next); + replaceWith: function replaceWith($element) { + elements.forEach(function(element) { + var parent = element.parentNode, + sibling = element.nextSibling; + if (parent) { + removeOxElements(element, true); + parent.removeChild(element); + $other.forEach(function(otherElement) { + parent.insertBefore(otherElement, sibling); + }); + } + }); return this; }, /*@ - replaceWith Replaces this element with another element - (element) -> This element - element Another element + show Shows all elements + () -> This DOM object @*/ - replaceWith: function($element) { - var next = this[0].nextSibling, parent = this[0].parentNode; - this.remove(); - parent.insertBefore($element[0], next); - return this; - }, - /*@ - show Shows this element - () -> This element - @*/ - show: function() { + show: function show() { return this.css({display: 'block'}); }, /*@ - siblings Returns all siblings of this element - () -> <[oh]> Sibling elements + siblings Returns all siblings of all elements + () -> <[h]> Siblings @*/ - siblings: function() { - var that = this; - return Ox.filter(this[0].parentNode.childNodes, function(element) { - return element !== that[0]; - }); + siblings: function siblings() { + return Ox.unique(elements.map(function(element) { + return Ox.filter( + element.parentNode.childNodes, + function(sibling) { + return sibling !== element; + } + ); + })); }, /*@ - text Gets or sets the text contents + some Tests if some elements satisfy a given condition + (test) -> True if some elements pass the test + test Test function + @*/ + some: function some() { + return Array.prototype.some.apply(elements, arguments); + }, + /*@ + text Gets or sets the text contents of all elements () -> The text contents - (text) -> This element + (text) -> This DOM object text The text contents @*/ - text: function(string) { - var ret; + text: function text(string) { + var text = ''; if (arguments.length == 0) { - ret = Ox.isString(this.textContent) - ? this.textContent : this.innerText; + elements.forEach(function(element) { + text += Ox.isString(this.textContent) ? this.textContent + : this.innerText; + }); + return text; } else { - this.empty()[0].appendChild(document.createTextNode(string)); - ret = this; + elements.forEach(function(element) { + element.empty(); + element.appendChild(document.createTextNode(string)); + }); + return this; } - return ret; }, /*@ - toggleClass Toggles a class name - (className) -> This element + toggleClass Toggles a class name for all elements + (className) -> This DOM object className Class name @*/ - toggleClass: function(string) { - return this[ - this.hasClass(string) ? 'removeClass' : 'addClass' - ](string); - }, - /*@ - trigger Triggers an event - (event) -> This element - @*/ - trigger: function(event) { - var e = document.createEvent('MouseEvents'); - e.initEvent(event, true, true); - this[0].dispatchEvent(e); + toggleClass: function toggleClass(string) { + elements.forEach(function(element) { + var $element = Ox.$(element); + $element[ + $element.hasClass(string) ? 'removeClass' : 'addClass' + ](string); + }) return this; }, /*@ - val Gets or sets the value property of this element - () -> Value - (value) -> This element - value Value + trigger Triggers an event + (event) -> This DOM object @*/ - val: function(value) { - var ret; - if (arguments.length == 0) { - ret = this[0].value; - } else { - this[0].value = value; - ret = this; - } - return ret; + trigger: function trigger(event) { + elements.forEach(function(element) { + var e = document.createEvent('MouseEvents'); + e.initEvent(event, true, true); + element.dispatchEvent(e); + }); + return this; }, /*@ - width Returns the width of this element + val Gets the value of the first or sets the value of all elements + () -> Value + (value) -> This DOM object + value Value + @*/ + val: function val(value) { + var ret; + if (arguments.length == 0) { + return elements[0].value; + } else { + elements.forEach(function(element) { + element.value = value; + }); + return this; + } + }, + /*@ + width Returns the width of the first element () -> Width in px @*/ - width: function() { - return this[0].offsetWidth; + width: function width() { + return elements[0].offsetWidth; } - } : null; + }) : null; };