[转帖]Mootools源码分析-14 -- Element-1
原帖地址:http://space.flash8.net/space/?18713/viewspace-402754.html
原作者:我佛山人
/*
对HTML Element的包装
*/
var Element = new Native({
//族名,为$type方法提供精准类型
name: 'Element',
//包装扩展的原型
legacy: window.Element,
//初始化,使用Document对象的newElement创建对象
initialize: function(tag, props) {
//先看有没有特殊的HTML Element
var konstructor = Element.Constructors.get(tag);
//对特殊的HTML Element对象特别处理
if (konstructor) return konstructor(props);
//如果提供标签名,根据标签名创建HTML Element对象
if (typeof tag == 'string') return document.newElement(tag, props);
//设置属性
return $(tag).set(props);
},
//执行后续处理,每为Element增加一个扩展,Elements也会同时实现
afterImplement: function(key, value) {
if (!Array[key]) Elements.implement(key, Elements.multi(key));
Element.Prototype[key] = value;
}
});
Element.Prototype = {$family: {name: 'element'}};
Element.Constructors = new Hash;
//同时对Element和Document对象进行扩展
Native.implement([Element, Document], {
//根据选择符选择一个子Element
getElement: function(selector, notrash) {
return $(this.getElements(selector, true)[0] || null, notrash);
},
//根据选择符选择子Element
getElements: function(tags, nocash) {
tags = tags.split(',');
var elements = [];
var ddup = (tags.length > 1);
tags.each(function(tag) {
var partial = this.getElementsByTagName(tag.trim());
(ddup) ? elements.extend(partial) : elements = partial;
}, this);
return new Elements(elements, {ddup: ddup, cash: !nocash});
}
});
//用于获取保存在当前Element下的临时对象,参考Fx.Tween和Drag
Element.Storage = {
get: function(uid) {
return (this[uid] || (this[uid] = {}));
}
};
//相当于当前Element的四个位置的DOM插入
Element.Inserters = new Hash({
//在context指代的Element之前插入eleemnt
before: function(context, element) {
if (element.parentNode) element.parentNode.insertBefore(context, element);
},
//在context指代的Element之后插入eleemnt
after: function(context, element) {
if (!element.parentNode) return;
var next = element.nextSibling;
(next) ? element.parentNode.insertBefore(context, next) : element.parentNode.appendChild(context);
},
//在context指代的Element内最后插入eleemnt
bottom: function(context, element) {
element.appendChild(context);
},
//在context指代的Element内首位插入eleemnt
top: function(context, element) {
var first = element.firstChild;
(first) ? element.insertBefore(context, first) : element.appendChild(context);
}
});
//别名
Element.Inserters.inside = Element.Inserters.bottom;
//生成injectTop/grabTop之类的快捷方式
Element.Inserters.each(function(value, key) {
var Key = key.capitalize();
//injectTop,injectBefore,injectAfter,injectBottom
Element.implement('inject' + Key, function(el) {
value(this, $(el, true));
return this;
});
//grabTop,grabBefore,grabAfter,grabBottom
Element.implement('grab' + Key, function(el) {
value($(el, true), this);
return this;
});
});
Element.implement({
//当前Element所属的Document
getDocument: function() {
return this.ownerDocument;
},
//当前Element所属的Window
getWindow: function() {
return this.ownerDocument.getWindow();
},
//根据ID查找当前Element下的对象
getElementById: function(id, nocash) {
var el = this.ownerDocument.getElementById(id);
if (!el) return null;
for (var parent = el.parentNode; parent != this; parent = parent.parentNode) {
if (!parent) return null;
}
return $.element(el, nocash);
},
//万能的setter
set: function(prop, value) {
switch ($type(prop)) {
case 'object':
for (var p in prop) this.set(p, prop[p]);
break;
case 'string':
var property = Element.Properties.get(prop);
(property && property.set) ? property.set.apply(this, Array.slice(arguments, 1)) : this.setProperty(prop, value);
}
return this;
},
//万能的getter
get: function(prop) {
var property = Element.Properties.get(prop);
return (property && property.get) ? property.get.apply(this, Array.slice(arguments, 1)) : this.getProperty(prop);
},
//万能的eraser
erase: function(prop) {
var property = Element.Properties.get(prop);
(property && property.erase) ? property.erase.apply(this, Array.slice(arguments, 1)) : this.removeProperty(prop);
return this;
},
//判断当前Element是否匹配指定标签名
match: function(tag) {
return (!tag || Element.get(this, 'tag') == tag);
},
//根据where指定的位置,将当前Element注入到el
inject: function(el, where) {
Element.Inserters.get(where || 'bottom')(this, $(el, true));
return this;
},
//根据where指定的位置,使当前Element作为el的父Element
wraps: function(el, where) {
el = $(el, true);
return this.replaces(el).grab(el, where);
},
//和inject相反
grab: function(el, where) {
Element.Inserters.get(where || 'bottom')($(el, true), this);
return this;
},
//追加文本
appendText: function(text, where) {
return this.grab(this.getDocument().newTextNode(text), where);
},
//将多个对象追加到当前Element内
adopt: function() {
Array.flatten(arguments).each(function(element) {
element = $(element, true);
if (element) this.appendChild(element);
}, this);
return this;
},
//从DOM节点中删除
dispose: function() {
return (this.parentNode) ? this.parentNode.removeChild(this) : this;
},
//从当前Element克隆出新的Element,contents指定是否包含子节点,keepid指定是否忽略id属性
clone: function(contents, keepid) {
switch ($type(this)) {
case 'element':
var attributes = {};
for (var j = 0, l = this.attributes.length; j < l; j++) {
var attribute = this.attributes[j], key = attribute.nodeName.toLowerCase();
var value = (key == 'style' && this.style) ? this.style.cssText : attribute.nodeValue;
if (!$chk(value) || key == 'uid' || (key == 'id' && !keepid)) continue;
if (value != 'inherit' && ['string', 'number'].contains($type(value))) attributes[key] = value;
}
var element = new Element(this.nodeName.toLowerCase(), attributes);
if (contents !== false) {
for (var i = 0, k = this.childNodes.length; i < k; i++) {
var child = Element.clone(this.childNodes[i], true, keepid);
if (child) element.grab(child);
}
}
return element;
//如果当前是文本节点
case 'textnode': return document.newTextNode(this.nodeValue);
}
return null;
},
//替换节点
replaces: function(el) {
el = $(el, true);
el.parentNode.replaceChild(this, el);
return this;
},
//是否使用指定css类名
hasClass: function(className) {
return this.className.contains(className, ' ');
},
//添加指定css类名
addClass: function(className) {
if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean();
return this;
},
//移除指定css类名
removeClass: function(className) {
this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1').clean();
return this;
},
//切换指定css类名
toggleClass: function(className) {
return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
},
//获取运行时的样式属性
getComputedStyle: function(property) {
//IE
if (this.currentStyle) return this.currentStyle[property.camelCase()];
//非IE
var computed = this.getWindow().getComputedStyle(this, null);
return (computed) ? computed.getPropertyValue([property.hyphenate()]) : null;
},
//清空当前Element,包括内部节点的删除和内存释放
empty: function() {
$A(this.childNodes).each(function(node) {
Element.empty(node);
this.removeChild(node);
memfree(node);
}, this);
return this;
},
//销毁当前Element,删除所有有关的一切
destroy: function() {
memfree(this.empty().dispose());
return null;
},
//获取选中的子项,仅当<select multi>有效
getSelected: function() {
return $A(this.options).filter(function(option){
return option.selected;
});
},
//将当前Element内的表单项数据转为查询串格式,以用于Ajax或get方式的提交
toQueryString: function() {
var queryString = [];
this.getElements('input, select, textarea').each(function(el) {
if (!el.name || el.disabled) return;
var value = (el.tagName.toLowerCase() == 'select') ? Element.getSelected(el).map(function(opt) {
return opt.value;
}) : ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) ? null : el.value;
$splat(value).each(function(val) {
if (val) queryString.push(el.name + '=' + encodeURIComponent(val));
});
});
return queryString.join('&');
},
//读取属性值,包括自定义属性
getProperty: function(attribute) {
var EA = Element.Attributes, key = EA.Props[attribute];
var value = (key) ? this[key] : this.getAttribute(attribute, 2);
return (EA.Bools[attribute]) ? !!value : (key) ? value : value || null;
},
//根据属性列表读取相应值,以objecct方式返回结果
getProperties: function() {
var args = $A(arguments);
return args.map(function(attr) {
return this.getProperty(attr);
}, this).associate(args);
},
//属性设置
setProperty: function(attribute, value) {
var EA = Element.Attributes, key = EA.Props[attribute], hasValue = $defined(value);
if (key && EA.Bools[attribute]) value = (value || !hasValue) ? true : false;
else if (!hasValue) return this.removeProperty(attribute);
(key) ? this[key] = value : this.setAttribute(attribute, value);
return this;
},
//批量属性设置
setProperties: function(attributes) {
for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
return this;
},
//移除属性
removeProperty: function(attribute) {
var EA = Element.Attributes, key = EA.Props[attribute], isBool = (key && EA.Bools[attribute]);
(key) ? this[key] = (isBool) ? false : '' : this.removeAttribute(attribute);
return this;
},
//批量移除属性
removeProperties: function() {
Array.each(arguments, this.removeProperty, this);
return this;
}
});
对HTML Element的包装
*/
var Element = new Native({
//族名,为$type方法提供精准类型
name: 'Element',
//包装扩展的原型
legacy: window.Element,
//初始化,使用Document对象的newElement创建对象
initialize: function(tag, props) {
//先看有没有特殊的HTML Element
var konstructor = Element.Constructors.get(tag);
//对特殊的HTML Element对象特别处理
if (konstructor) return konstructor(props);
//如果提供标签名,根据标签名创建HTML Element对象
if (typeof tag == 'string') return document.newElement(tag, props);
//设置属性
return $(tag).set(props);
},
//执行后续处理,每为Element增加一个扩展,Elements也会同时实现
afterImplement: function(key, value) {
if (!Array[key]) Elements.implement(key, Elements.multi(key));
Element.Prototype[key] = value;
}
});
Element.Prototype = {$family: {name: 'element'}};
Element.Constructors = new Hash;
//同时对Element和Document对象进行扩展
Native.implement([Element, Document], {
//根据选择符选择一个子Element
getElement: function(selector, notrash) {
return $(this.getElements(selector, true)[0] || null, notrash);
},
//根据选择符选择子Element
getElements: function(tags, nocash) {
tags = tags.split(',');
var elements = [];
var ddup = (tags.length > 1);
tags.each(function(tag) {
var partial = this.getElementsByTagName(tag.trim());
(ddup) ? elements.extend(partial) : elements = partial;
}, this);
return new Elements(elements, {ddup: ddup, cash: !nocash});
}
});
//用于获取保存在当前Element下的临时对象,参考Fx.Tween和Drag
Element.Storage = {
get: function(uid) {
return (this[uid] || (this[uid] = {}));
}
};
//相当于当前Element的四个位置的DOM插入
Element.Inserters = new Hash({
//在context指代的Element之前插入eleemnt
before: function(context, element) {
if (element.parentNode) element.parentNode.insertBefore(context, element);
},
//在context指代的Element之后插入eleemnt
after: function(context, element) {
if (!element.parentNode) return;
var next = element.nextSibling;
(next) ? element.parentNode.insertBefore(context, next) : element.parentNode.appendChild(context);
},
//在context指代的Element内最后插入eleemnt
bottom: function(context, element) {
element.appendChild(context);
},
//在context指代的Element内首位插入eleemnt
top: function(context, element) {
var first = element.firstChild;
(first) ? element.insertBefore(context, first) : element.appendChild(context);
}
});
//别名
Element.Inserters.inside = Element.Inserters.bottom;
//生成injectTop/grabTop之类的快捷方式
Element.Inserters.each(function(value, key) {
var Key = key.capitalize();
//injectTop,injectBefore,injectAfter,injectBottom
Element.implement('inject' + Key, function(el) {
value(this, $(el, true));
return this;
});
//grabTop,grabBefore,grabAfter,grabBottom
Element.implement('grab' + Key, function(el) {
value($(el, true), this);
return this;
});
});
Element.implement({
//当前Element所属的Document
getDocument: function() {
return this.ownerDocument;
},
//当前Element所属的Window
getWindow: function() {
return this.ownerDocument.getWindow();
},
//根据ID查找当前Element下的对象
getElementById: function(id, nocash) {
var el = this.ownerDocument.getElementById(id);
if (!el) return null;
for (var parent = el.parentNode; parent != this; parent = parent.parentNode) {
if (!parent) return null;
}
return $.element(el, nocash);
},
//万能的setter
set: function(prop, value) {
switch ($type(prop)) {
case 'object':
for (var p in prop) this.set(p, prop[p]);
break;
case 'string':
var property = Element.Properties.get(prop);
(property && property.set) ? property.set.apply(this, Array.slice(arguments, 1)) : this.setProperty(prop, value);
}
return this;
},
//万能的getter
get: function(prop) {
var property = Element.Properties.get(prop);
return (property && property.get) ? property.get.apply(this, Array.slice(arguments, 1)) : this.getProperty(prop);
},
//万能的eraser
erase: function(prop) {
var property = Element.Properties.get(prop);
(property && property.erase) ? property.erase.apply(this, Array.slice(arguments, 1)) : this.removeProperty(prop);
return this;
},
//判断当前Element是否匹配指定标签名
match: function(tag) {
return (!tag || Element.get(this, 'tag') == tag);
},
//根据where指定的位置,将当前Element注入到el
inject: function(el, where) {
Element.Inserters.get(where || 'bottom')(this, $(el, true));
return this;
},
//根据where指定的位置,使当前Element作为el的父Element
wraps: function(el, where) {
el = $(el, true);
return this.replaces(el).grab(el, where);
},
//和inject相反
grab: function(el, where) {
Element.Inserters.get(where || 'bottom')($(el, true), this);
return this;
},
//追加文本
appendText: function(text, where) {
return this.grab(this.getDocument().newTextNode(text), where);
},
//将多个对象追加到当前Element内
adopt: function() {
Array.flatten(arguments).each(function(element) {
element = $(element, true);
if (element) this.appendChild(element);
}, this);
return this;
},
//从DOM节点中删除
dispose: function() {
return (this.parentNode) ? this.parentNode.removeChild(this) : this;
},
//从当前Element克隆出新的Element,contents指定是否包含子节点,keepid指定是否忽略id属性
clone: function(contents, keepid) {
switch ($type(this)) {
case 'element':
var attributes = {};
for (var j = 0, l = this.attributes.length; j < l; j++) {
var attribute = this.attributes[j], key = attribute.nodeName.toLowerCase();
var value = (key == 'style' && this.style) ? this.style.cssText : attribute.nodeValue;
if (!$chk(value) || key == 'uid' || (key == 'id' && !keepid)) continue;
if (value != 'inherit' && ['string', 'number'].contains($type(value))) attributes[key] = value;
}
var element = new Element(this.nodeName.toLowerCase(), attributes);
if (contents !== false) {
for (var i = 0, k = this.childNodes.length; i < k; i++) {
var child = Element.clone(this.childNodes[i], true, keepid);
if (child) element.grab(child);
}
}
return element;
//如果当前是文本节点
case 'textnode': return document.newTextNode(this.nodeValue);
}
return null;
},
//替换节点
replaces: function(el) {
el = $(el, true);
el.parentNode.replaceChild(this, el);
return this;
},
//是否使用指定css类名
hasClass: function(className) {
return this.className.contains(className, ' ');
},
//添加指定css类名
addClass: function(className) {
if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean();
return this;
},
//移除指定css类名
removeClass: function(className) {
this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1').clean();
return this;
},
//切换指定css类名
toggleClass: function(className) {
return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
},
//获取运行时的样式属性
getComputedStyle: function(property) {
//IE
if (this.currentStyle) return this.currentStyle[property.camelCase()];
//非IE
var computed = this.getWindow().getComputedStyle(this, null);
return (computed) ? computed.getPropertyValue([property.hyphenate()]) : null;
},
//清空当前Element,包括内部节点的删除和内存释放
empty: function() {
$A(this.childNodes).each(function(node) {
Element.empty(node);
this.removeChild(node);
memfree(node);
}, this);
return this;
},
//销毁当前Element,删除所有有关的一切
destroy: function() {
memfree(this.empty().dispose());
return null;
},
//获取选中的子项,仅当<select multi>有效
getSelected: function() {
return $A(this.options).filter(function(option){
return option.selected;
});
},
//将当前Element内的表单项数据转为查询串格式,以用于Ajax或get方式的提交
toQueryString: function() {
var queryString = [];
this.getElements('input, select, textarea').each(function(el) {
if (!el.name || el.disabled) return;
var value = (el.tagName.toLowerCase() == 'select') ? Element.getSelected(el).map(function(opt) {
return opt.value;
}) : ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) ? null : el.value;
$splat(value).each(function(val) {
if (val) queryString.push(el.name + '=' + encodeURIComponent(val));
});
});
return queryString.join('&');
},
//读取属性值,包括自定义属性
getProperty: function(attribute) {
var EA = Element.Attributes, key = EA.Props[attribute];
var value = (key) ? this[key] : this.getAttribute(attribute, 2);
return (EA.Bools[attribute]) ? !!value : (key) ? value : value || null;
},
//根据属性列表读取相应值,以objecct方式返回结果
getProperties: function() {
var args = $A(arguments);
return args.map(function(attr) {
return this.getProperty(attr);
}, this).associate(args);
},
//属性设置
setProperty: function(attribute, value) {
var EA = Element.Attributes, key = EA.Props[attribute], hasValue = $defined(value);
if (key && EA.Bools[attribute]) value = (value || !hasValue) ? true : false;
else if (!hasValue) return this.removeProperty(attribute);
(key) ? this[key] = value : this.setAttribute(attribute, value);
return this;
},
//批量属性设置
setProperties: function(attributes) {
for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
return this;
},
//移除属性
removeProperty: function(attribute) {
var EA = Element.Attributes, key = EA.Props[attribute], isBool = (key && EA.Bools[attribute]);
(key) ? this[key] = (isBool) ? false : '' : this.removeAttribute(attribute);
return this;
},
//批量移除属性
removeProperties: function() {
Array.each(arguments, this.removeProperty, this);
return this;
}
});