;(function(global, S) {
;(function(global, S) {
Node 模块是 DOM 节点相关操作的核心封装,对外暴露一个全局的构造函数S.Node
,同时对外暴露一个掺元类S.node
,即如果希望一个对象(DOM节点)拥有S.Node
的原型方法,则可以使用S.node
来mix进这些方法
但通常我们不会直接使用S.node
,涉及到对象创建时可直接使用S.Node()
构造器,比如创建一个节点
// 创建一个span,然后挂在到body下
S.Node('<span>hello</span>').appendTo(document.body);
还有一个简便用法就是直接使用S.all
或者S.one
:
$ = S.all;
$('<span>hello</span>').appendTo(document.body);
关于$
KISSY 全局对象上直接提供了两个最常用的便利节点的方法
通常设置$ = KISSY.all
即可
$ = KISSY.all;
$('#id').append('<span>hello</span>');
API | KISSY | KISSY-MINI |
---|---|---|
test | √ | ╳ |
replaceClass | √ | ╳ |
style | √ | ╳ |
innerWidth | √ | ╳ |
innerHeight | √ | ╳ |
outerWidth | √ | ╳ |
outerHeight | √ | ╳ |
addStyleSheet | √ | ╳ |
docHeight | √ | ╳ |
docWidth | √ | ╳ |
viewportHeight | √ | ╳ |
viewportWidth | √ | ╳ |
scrollIntoView | √ | ╳ |
unselectable | √ | ╳ |
nodeName | √ | ╳ |
outerHTML | √ | ╳ |
API | KISSY | KISSY-MINI |
---|---|---|
data | √ | ╳ |
removeData | √ | ╳ |
hasData | √ | ╳ |
KISSY | KISSY-MINI | Note |
---|---|---|
S.DOM.css(el, name) | S.all(el).css(name) | 只支持链式写法 |
S.DOM.parent(el, 2) | ╳ | 不支持指定层级 |
S.DOM.clone() | 只支持元素复制 | |
/**
* @ignore
* @file util
* @author 莫争 <gaoli.gl@taobao.com>
*/
var win = window,
doc = document,
docEl = doc.documentElement;
var emptyArray = [],
some = emptyArray.some,
every = emptyArray.every,
slice = emptyArray.slice,
filter = emptyArray.filter,
concat = emptyArray.concat,
indexOf = emptyArray.indexOf,
forEach = emptyArray.forEach;
function mix(target, source) {
for (var key in source) {
target[key] = source[key];
}
}
function map(els, cb) {
var val,
ret = [];
els && forEach.call(els, function(el, index) {
val = cb(el, index);
if (val !== null) {
ret.push(val);
}
});
return ret.length ? concat.apply([], ret) : ret;
}
function each(els, callback) {
els && forEach.call(els, callback);
return els;
}
function isWindow(node) {
return node && node == node.window;
}
function isDocument(node) {
return node && node.nodeType === 9;
}
function isElement(node) {
return node && node.nodeType === 1;
}
function likeArray(nodes) {
return nodes && typeof nodes.length == 'number';
}
function unique(array) {
return filter.call(array, function(item, index) {
return array.indexOf(item) == index;
});
}
function getScript(url) {
var script = doc.createElement('script'),
head = doc.getElementsByTagName('head')[0] || docEl;
script.src = url;
head.insertBefore(script, head.firstChild);
}
function isType(type) {
return function(obj) {
return {}.toString.call(obj) == '[object ' + type + ']';
}
}
var isNumber = isType('Number'),
isString = isType('String'),
isObject = isType('Object'),
isArray = Array.isArray || isType('Array'),
isFunction = isType('Function');
var isPlainObject = function(obj) {
return isObject(obj) && !isWindow(obj) && Object.getPrototypeOf(obj) == Object.prototype;
};
/**
* @ignore
* @file node
* @author 莫争 <gaoli.gl@taobao.com>
*/
var node = {};
mix(node, {
indexOf: function(el) {
return likeArray(el) ?
indexOf.call(this, el[0]) :
indexOf.apply(this, arguments);
},
each: function(cb) {
every.call(this, function(el, index) {
el = $(el);
return cb.call(el, el, index) !== false;
});
return this;
},
slice: function() {
return $(slice.apply(this, arguments));
},
.end()
得到上一次 S.one() / S.all() 操作前的 Node 对象
引入该方法是为了更好的支持链式操作( chaining )
可以在一个语句内对不同层次得节点集合进行不同的操作
end: function() {
return this.__parent || this;
},
getDOMNode: function() {
return this[0]
}
});
.all()
通过执行 css 选择器包装 dom 节点,创建元素或者从一个 html 片段来创建一个 Node 对象
Node 集合是一个类似数组的对象,它具有链式方法来操作它指向的 dom ,文档对象中的所有方法都是集合方法
如果选择器中存在 content 参数(css 选择器,dom,或者 Node 集合对象),那么只在所给的节点背景下进行 css 选择器,这个功能有点像使用 S.all(context).all(selector)
可以通过一个 html 字符串片段来创建一个 dom 节点。也可以通过给定一组属性映射来创建节点。最快的创建元素,使用 <div>
或 <div/>
形式
var $ = S.all;
$('div'); //=> 获取所有 div 节点
$('#foo'); //=> 获取 ID 为 'foo' 的节点
$('<p>Hello</p>'); //=> 创建 p 节点
$('<div />', {
text: 'ok',
css : {
color: 'red'
}
}); //=> <div style="color:red">ok</div>
var $ = function(selector, context) {
var ret = [];
if (selector) {
if (isString(selector)) {
selector = selector.trim();
if (selector[0] == '<' && /<([\w:]+)/.test(selector)) {
ret = node.create(selector);
} else if (context !== undefined) {
ret = find(selector, $(context));
} else {
ret = query(selector, doc);
}
} else if ($.isNode(selector)) {
return selector;
} else {
if (selector.nodeType || selector.setTimeout) {
ret = [selector];
} else if (isArray(selector)) {
ret = [];
each(selector, function(s) {
ret.push($.isNode(s) ? s[0] : s);
});
} else if (!selector.nodeType && !selector.setTimeout && selector.item) {
ret = slice.call(selector);
}
}
}
return $.node(ret);
};
$.all = function(selector, context) {
return $(selector, context);
};
$.one = function(selector, context) {
return $(selector, context).item(0);
};
.node()
把一个节点(数组)转换为 Node(集合)对象
这里 $.node
实际指的是 S.Node.node
,Node 对象实例将会继承 S.node
里的方法
S.Node.node
一般情况下可以使用 S.all()
来代替
$.node = function(els) {
els = els || [];
els.__proto__ = node;
return els;
};
$.node.prototype = node;
$.isNode = function(obj) {
return obj instanceof $.node;
};
/**
* @ignore
* @file node-selector
* @author 莫争 <gaoli.gl@taobao.com>
*/
var tempParent = document.createElement('div');
function find(selector, context) {
return context.length === 1 ?
query(selector, context[0]) :
unique(
map(context, function(el) {
return query(selector, el);
})
);
}
function query(selector, context) {
if(typeof selector == 'object' && selector[0]){
if(isElement(context) && context.contains($(selector[0]).getDOMNode())){
return selector;
}
}
var s = selector.charAt(0), ret,
maybeID = s === '#',
maybeCls = s === '.',
nameOnly = maybeID || maybeCls ? selector.slice(1) : selector,
isSimple = /^[\w-]*$/.test(nameOnly);
context = context === undefined ? document : context;
return $(
isDocument(context) || isElement(context) ?
isDocument(context) && maybeID && isSimple ?
(ret = context.getElementById(nameOnly)) ? [ret] : [] :
slice.call(
!maybeID && isSimple ?
maybeCls ?
context.getElementsByClassName(nameOnly) :
context.getElementsByTagName(selector) :
context.querySelectorAll(selector)
)
: []
);
}
function matches(el, selector) {
if (!el || !selector || !isElement(el)) {
return false;
}
var matchesSelector = el.webkitMatchesSelector ||
el.mozMatchesSelector ||
el.oMatchesSelector ||
el.matchesSelector;
if (matchesSelector) {
return matchesSelector.call(el, selector);
} else {
var parent = el.parentNode,
hasParent = !!parent,
match;
if (!hasParent) {
parent = tempParent;
parent.appendChild(el);
}
match = ~query(selector, parent).indexOf(el);
!hasParent && parent.removeChild(el);
return match;
}
}
mix(S, {
query: query
});
mix(node, {
all: function(selector) {
var self = this,
ret;
ret = $(find(selector, self));
ret.__parent = self;
return ret;
},
one: function(selector) {
var self = this,
ret;
ret = self.all(selector);
ret = ret.length ? ret.slice(0, 1) : null;
if (ret) {
ret.__parent = self;
}
return ret;
},
filter: function(selector) {
if (isFunction(selector)) {
return $(filter.call(this, function(el) {
return selector.call(el, el);
}));
} else {
return $(filter.call(this, function(el) {
return matches(el, selector);
}));
}
}
});
/**
* @ignore
* @file node-class
* @author 莫争 <gaoli.gl@taobao.com>
*/
var classCache = {};
function classRE(name) {
return name in classCache ?
classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)'));
}
function className(el, val) {
if (val === undefined) {
return el.className;
} else {
el.className = val;
}
}
function classSplit(names) {
return names.split(/[\.\s]\s*\.?/);
}
mix(node, {
addClass: function(names) {
if (!names) return this;
return each(this, function(el) {
var $el = $(el),
cls = className(el),
classList = [];
each(classSplit(names), function(name) {
!$el.hasClass(name) && classList.push(name)
});
classList.length && className(el, cls + (cls ? ' ' : '') + classList.join(' '))
});
},
.removeClass()
.removeClass()
给符合选择器的所有元素移除所有 class
.removeClass(names)
给符合选择器的所有元素移除指定 class
多个 class 类名通过空格分隔
removeClass: function(names) {
return each(this, function(el) {
if (names === undefined) {
return className(el, '');
} else {
var classList = className(el);
each(classSplit(names), function(name) {
classList = classList.replace(classRE(name), ' ');
});
className(el, classList.trim());
}
});
},
.toggleClass()
.toggleClass(names[, when])
操作符合选择器的所有元素,如果存在值为 names 的 class,则移除掉,反之添加
如果 when 的值为真,这个功能类似于 .addClass()
方法,如果为假,这个功能类似与 .removeClass()
方法
toggleClass: function(names, when) {
if (!names) return this;
return each(this, function(el) {
var $el = $(el);
each(classSplit(names), function(name) {
(when === undefined ? !$el.hasClass(name) : when) ?
$el.addClass(name) : $el.removeClass(name);
});
});
},
hasClass: function(names) {
if (!names) return false;
return some.call(this, function(el) {
return every.call(this, function(name) {
return name ? classRE(name).test(className(el)) : true;
});
}, classSplit(names));
}
});
/**
* @ignore
* @file node-attr
* @author 莫争 <gaoli.gl@taobao.com>
*/
var RE_BOOLEAN = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i;
var attrMethod = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset'],
propFixMap = {
hidefocus : 'hideFocus',
tabindex : 'tabIndex',
readonly : 'readOnly',
'for' : 'htmlFor',
'class' : 'className',
maxlength : 'maxLength',
cellspacing : 'cellSpacing',
cellpadding : 'cellPadding',
rowspan : 'rowSpan',
colspan : 'colSpan',
usemap : 'useMap',
frameborder : 'frameBorder',
contenteditable: 'contentEditable'
};
function pluck(els, property) {
return map(els, function(el) {
return el[property];
});
}
function setAttribute(el, name, val) {
val == null ? el.removeAttribute(name) : el.setAttribute(name, val)
}
mix(node, {
.attr()
.attr(name)
获取符合选择器的第一个元素的属性值
.attr(name, val)
给符合选择器的所有元素设置属性值
.attr(kv)
给符合选择器的所有元素设置属性值
.attr()
和 .prop()
的区别
el.attr('checked') // => "checked"
el.prop('checked') // => true
.attr()
和 .prop()
的使用
从中文意思看,两者分别是获取 / 设置 attributes 和 properties 的方法,分别在这两个场景中使用:具有 true 和 false 两个属性的属性,如 checked,selected 或者 disabled 使用 .prop()
,其他的使用 .attr()
attr: function(name, val) {
var key,
ret;
if (isPlainObject(name)) {
for (key in name) {
node.attr.call(this, key, name[key]);
}
return this;
}
if (~attrMethod.indexOf(name)) {
return $(this)[name](val);
}
if (val == undefined) {
var el = this[0];
if (el && isElement(el)) {
if (RE_BOOLEAN.test(name)) {
ret = $(el).prop(name) ? name.toLowerCase() : undefined;
} else if (name == 'value' && el.nodeName == 'INPUT') {
ret = this.val();
} else {
ret = el.getAttribute(name);
ret = !ret && name in el ? el[name] : ret;
}
}
} else {
ret = each(this, function(el) {
isElement(el) && setAttribute(el, name, val);
});
}
return ret === null ? undefined : ret;
},
removeAttr: function(name) {
return each(this, function(el) {
isElement(el) && setAttribute(el, name)
});
},
hasAttr: function(name) {
if (!name) return false;
return some.call(this, function(el) {
return isElement(el) && el.getAttribute(name);
});
},
.prop()
.prop(name)
获取符合选择器的第一个元素的对应 property 值
.prop(name, val)
给符合选择器的所有元素设置 property 值
.prop(kv)
给符合选择器的所有元素设置 property 值
prop: function(name, val) {
name = propFixMap[name] || name;
return val == undefined ?
this[0] && this[0][name] :
each(this, function(el) {
el[name] = val;
});
},
hasProp: function(name) {
if (!name) return false;
name = propFixMap[name] || name;
return some.call(this, function(el) {
return isElement(el) && el[name];
});
},
.val()
.val()
获取符合选择器的第一个元素所的 value 值
.val(val)
给符合选择器的所有元素设置 value 值
如果是 <select multiple>
标签,则返回一个数组
val: function(val) {
var el = this[0];
if (!el) return this;
if (el.multiple) {
var opts = $('option', el);
return val == undefined ?
slice.call(
pluck(
opts.filter(function(opt) {
return opt.selected;
}), 'value'
)
) :
each(opts, function(opt) {
opt.selected = ~val.indexOf(opt.value);
});
} else {
return val == undefined ?
el.value :
each(this, function(el) {
el.value = val;
});
}
},
text: function(text) {
return text === undefined ?
this.length ?
this[0].textContent : null
:
each(this, function(el) {
el.textContent = (text === undefined) ? '' : '' + text
});
}
});
/**
* @ignore
* @file node-style
* @author 莫争 <gaoli.gl@taobao.com>
*/
var cssNumber = {
'column-count': 1,
'columns' : 1,
'font-weight' : 1,
'line-height' : 1,
'opacity' : 1,
'z-index' : 1,
'zoom' : 1
},
elDisplay = {};
function dasherize(str) {
return str.replace(/::/g, '/')
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
.replace(/([a-z\d])([A-Z])/g, '$1_$2')
.replace(/_/g, '-')
.toLowerCase();
}
.camelCase()
.camelCase(name)
内部方法,将一组字符串变成“骆驼”命名法的新字符串,如果该字符已经是“骆驼”命名法,则不变化
.camelCase('abc-def'); //=> 'abcDef'
.camelCase('abcDef'); //=> 'abcDef'
function camelCase(name) {
return name.replace(/-+(.)?/g, function() {
return arguments[1].toUpperCase();
});
}
function maybeAddPx(name, val) {
return isNumber(val) && !cssNumber[dasherize(name)] ? val + 'px' : val;
}
function getComputedStyle(el, name) {
return win.getComputedStyle(el, null).getPropertyValue(name);
}
function getDefaultDisplay(tagName) {
if (!elDisplay[tagName]) {
var el = doc.createElement(tagName),
display;
doc.body.appendChild(el);
display = getComputedStyle(el, 'display');
el.parentNode.removeChild(el);
display == 'none' && (display = 'block');
elDisplay[tagName] = display;
}
return elDisplay[tagName];
}
mix(node, {
.css()
.css(name)
获取符合选择器的第一个元素的样式值
.css(name, val)
给符合选择器的所有元素设置样式值
.css(kv)
给符合选择器的所有元素设置样式值
var el = $('h1');
el.css('background-color'); // 获取属性
el.css('background-color', '#369'); // 设置属性
el.css('background-color', ''); // 删除属性
// 设置多个属性
el.css({
fontSize : 28,
backgroundColor: '#8EE'
});
css: function(name, val) {
var key,
cssProp,
ret = '';
if (val == undefined) {
if (isString(name)) {
var el = this[0];
return el ? el.style[camelCase(name)] || getComputedStyle(el, name) : '';
} else if (isObject(name)) {
for (key in name) {
cssProp = dasherize(key);
if (!name[key] && name[key] !== 0) {
each(this, function(el) {
el.style.removeProperty(cssProp);
});
} else {
ret += cssProp + ':' + maybeAddPx(key, name[key]) + ';';
}
}
}
} else {
cssProp = dasherize(name);
if (!val && val !== 0) {
each(this, function(el) {
el.style.removeProperty(cssProp);
});
} else {
ret = cssProp + ':' + maybeAddPx(name, val) + ';';
}
}
return each(this, function(el) {
el.style.cssText += ';' + ret;
});
},
show: function() {
return each(this, function(el) {
el.style.display == 'none' && (el.style.display = '');
if (getComputedStyle(el, 'display') == 'none') {
el.style.display = getDefaultDisplay(el.nodeName);
}
});
},
hide: function() {
return this.css('display', 'none');
},
toggle: function() {
return each(this, function(el) {
var $el = $(el);
$el.css('display') == 'none' ? $el.show() : $el.hide();
});
}
});
.width()
.width()
获取符合选择器的第一个元素的宽度值
.width(val)
给符合选择器的所有元素设置宽度值
.width()
和 .css('width')
的区别
.width()
返回不带单位的纯数值,.css('width')
返回带单位的原始值(例如:400px),当需要数值计算时, 推荐该方法.
获取 window
和 document
的宽度
// 获取当前可视区域的宽度值, 相当于 viewportWidth
S.all(window).width();
// 获取页面文档 document 的总宽度, 相当于 docWidth
S.all(document).width();
.height()
.height()
获取符合选择器的第一个元素的高度值
.height(val)
给符合选择器的所有元素设置高度值
获取 window
和 document
的高度
// 获取当前可视区域的高度值, 相当于 viewportHeight
S.all(window).height();
// 获取页面文档 document 的总高度, 相当于 docHeight
S.all(document).height();
each(['width', 'height'], function(method) {
node[method] = function(val) {
var el = this[0];
if (val) {
return $(this).css(method, val);
} else {
return isWindow(el) ? el[camelCase('inner-' + method)] :
isDocument(el) ? docEl[camelCase('scroll-' + method)] :
this.offset()[method];
}
};
});
/**
* @ignore
* @file node-traversal
* @author 莫争 <gaoli.gl@taobao.com>
*/
function filtered(els, selector) {
var $els = $(els);
return selector !== undefined ?
$els.filter(
isArray(selector) ?
function(el) {
return some.call(selector, function(filter) {
return matches(el, filter);
});
} :
selector
) :
$els;
}
function children(el) {
return 'children' in el ?
slice.call(el.children) :
map(el.childNodes, function(el) {
if (isElement(el)) {
return el;
}
});
}
function nth(el, filter, property, includeSelf) {
var ret = [],
array = isArray(filter);
el = includeSelf ? el : el[property];
while (el) {
if (el && !isDocument(el) && isElement(el) && ret.indexOf(el) < 0) {
ret.push(el);
}
el = el[property];
}
if (array && !filter.length) {
filter = undefined
}
ret = filtered(ret, filter);
return array ?
ret :
ret.item(0);
}
mix(node, {
item: function(index) {
var self = this;
return isNumber(index) ?
index >= self.length ?
null :
$(self[index]) :
$(index);
},
first: function(filter) {
return nth(this[0] && this[0].firstChild, filter, 'nextElementSibling', true);
},
last: function(filter) {
return nth(this[0] && this[0].lastChild, filter, 'previousElementSibling', true);
},
next: function(filter) {
return nth(this[0], filter, 'nextElementSibling');
},
prev: function(filter) {
return nth(this[0], filter, 'previousElementSibling');
},
parent: function(filter) {
return nth(this[0], filter, 'parentNode');
},
children: function(selector) {
var el = this[0];
return el ?
filtered(children(el), selector) :
this;
},
siblings: function(selector) {
var el = this[0];
return el ?
filtered(
filter.call(children(el.parentNode), function(child) {
return child !== el;
})
, selector) :
this;
},
contents: function() {
var el = this[0];
return el ?
$(slice.call(el.childNodes)) :
this;
},
contains: function(contained) {
var container = this[0],
contained = likeArray(contained) ? contained[0] : contained;
return container && contained ?
container !== contained && container.contains(contained) :
false;
}
});
/**
* @ignore
* @file node-insertion
* @author 莫争 <gaoli.gl@taobao.com>
*/
function filterScripts(nodes, scripts) {
var ret = [];
each(nodes, function(node) {
var name = node.nodeName,
type = node.type,
temp = [];
if (name && name === 'SCRIPT' && (!type || type === 'text/javascript')) {
node.parentNode && node.parentNode.removeChild(node);
scripts && scripts.push(node);
} else {
if (isElement(node)) {
each(node.getElementsByTagName('script'), function(el) {
temp.push(el);
});
filterScripts(temp, scripts);
}
ret.push(node);
}
});
return ret;
}
function nodeListToFragment(nodes) {
var ret = null;
if (nodes && likeArray(nodes)) {
ret = doc.createDocumentFragment();
each(nodes, function(node) {
ret.appendChild(node);
});
}
return ret;
}
mix(node, {
.wrapAll()
.wrapAll(wrapperNode)
在所有匹配元素外面包一层 HTML 结构
<div class="container">
<div class="inner">Hello</div>
<div class="inner">Goodbye</div>
</div>
S.all('.inner').wrapAll('<div class="new" />'); //=>
<div class="container">
<div class="new">
<div class="inner">Hello</div>
<div class="inner">Goodbye</div>
</div>
</div>
wrapAll: function(wrapperNode) {
var el = this[0];
if (el) {
var $wrapperNode = $(wrapperNode),
$wrapperChildren;
$wrapperNode.insertBefore(el);
while (($wrapperChildren = $wrapperNode.children()).length) {
$wrapperNode = $wrapperNode.first();
}
$wrapperNode.append(this);
}
return this;
},
.wrap()
.wrap(wrapperNode)
在每个匹配的元素外层包上一个 HTML 元素
<div class="container">
<div class="inner">Hello</div>
<div class="inner">Goodbye</div>
</div>
S.all('.inner').wrap('<div class="new" />'); //=>
<div class="container">
<div class="new">
<div class="inner">Hello</div>
</div>
<div class="new">
<div class="inner">Goodbye</div>
</div>
</div>
wrap: function(wrapperNode) {
var $wrapperNode = $(wrapperNode),
wrapperClone = $wrapperNode[0].parentNode || this.length;
return each(this, function(el) {
$(el).wrapAll(
wrapperClone ? $wrapperNode[0].cloneNode(true) : $wrapperNode[0]
);
});
},
.unwrap()
.unwrap()
移除集合中每个元素的直接父节点,并把他们的子元素保留在原来的位置
<div class="container">
<div class="new">
<div class="inner">Hello</div>
</div>
<div class="new">
<div class="inner">Goodbye</div>
</div>
</div>
S.all('.inner').unwrap(); //=>
<div class="container">
<div class="inner">Hello</div>
<div class="inner">Goodbye</div>
</div>
unwrap: function() {
return each(this, function(el) {
var $el = $(el),
$parent = $el.parent();
$parent.replaceWith($parent.children());
});
},
.wrapInner()
.wrapInner(wrapperNode)
将每个元素中的内容包裹在一个单独的结构中
<div class="container">
<div class="inner">Hello</div>
<div class="inner">Goodbye</div>
</div>
S.all('.inner').wrapInner('<div class="new" />'); //=>
<div class="container">
<div class="inner">
<div class="new">Hello</div>
</div>
<div class="inner">
<div class="new">Goodbye</div>
</div>
</div>
wrapInner: function(wrapperNode) {
return each(this, function(el) {
var $el = $(el),
$children = $el.children();
if ($children.length) {
$children.wrapAll(wrapperNode);
} else {
$el.append(wrapperNode);
}
});
},
replaceWith: function(newNodes) {
return this.before(newNodes).remove();
}
});
.after()
.after(html)
在匹配元素集合中的每个元素后面插入内容,作为其兄弟节点
内容可以为 HTML字符串,DOM 元素,DOM元素数组
.prepend()
.prepend(html)
将内容插入到每个匹配元素的前面(元素内部)
内容可以为 HTML字符串,DOM 元素,DOM元素数组
.before()
.before(html)
在匹配元素的前面(元素外部)插入内容
内容可以为 HTML字符串,DOM 元素,DOM元素数组
.append()
.append(html)
在每个匹配元素里面的末尾处插入内容
内容可以为 HTML字符串,DOM 元素,DOM元素数组,包含DOM元素的数组,包含Node元素的数组
each(['after', 'prepend', 'before', 'append'], function(method, index) {
var inside = index % 2;
node[method] = function(html) {
var nodes = $.isNode(html) ? html : (isArray(html) ? $.all(html) : node.create(html + '')),
isCopy = this.length > 1,
scripts = [],
parent,
target;
if (nodes.length) {
nodes = nodeListToFragment(filterScripts(nodes, scripts));
} else {
return this;
}
return each(this, function(el) {
parent = inside ? el : el.parentNode;
switch (index) {
case 0:
target = el.nextSibling;
break;
case 1:
target = el.firstChild;
break;
case 2:
target = el;
break;
default:
target = null;
}
parent.insertBefore(isCopy ? nodes.cloneNode(true) : nodes, target);
each(scripts, function(el) {
if (el.src) {
getScript(el.src);
} else {
win['eval'].call(win, el.innerHTML);
}
});
});
};
.insertAfter()
.insertAfter(target)
将集合中的元素插入到指定的目标元素后面(外部插入)
.prependTo()
.prependTo(target)
将所有元素插入到目标前面(内部插入)
.insertBefore()
.insertBefore(target)
将集合中的元素插入到指定的目标元素前面(外部插入)
.appendTo()
.appendTo(target)
将匹配的元素插入到目标元素的末尾(内部插入)
node[inside ? method + 'To' : 'insert' + (index ? 'Before' : 'After')] = function(target) {
$(target)[method](this);
return this;
};
});
/**
* @ignore
* @file node-offset
* @author 莫争 <gaoli.gl@taobao.com>
*/
mix(node, {
offset: function(coordinates) {
var ret;
if (this.length) {
if (coordinates === undefined) {
var obj = this[0].getBoundingClientRect();
ret = {
left : obj.left + win.pageXOffset,
top : obj.top + win.pageYOffset,
width : Math.round(obj.width),
height: Math.round(obj.height)
}
} else {
each(this, function(el) {
var ret = {},
$el = $(el),
old = $el.offset(),
key,
current;
if ($el.css('position') === 'static') {
$el.css('position', 'relative');
}
for (key in coordinates) {
current = parseFloat($el.css(key)) || 0;
ret[key] = current + coordinates[key] - old[key];
}
$el.css(ret);
});
return this;
}
}
return ret;
}
});
.scrollTop()
.scrollTop()
获取窗口或元素的 scrollTop 值
.scrollTop(val)
设置窗口或元素的 scrollTop 值
.scrollLeft()
.scrollLeft()
获取窗口或元素的 scrollLeft 值
.scrollLeft(val)
设置窗口或元素的 scrollLeft 值
each(['scrollTop', 'scrollLeft'], function(method, index) {
node[method] = function(val) {
var el = this[0],
hasScroll = method in el;
return val === undefined ?
hasScroll ?
el[method] :
el['page' + (index ? 'X' : 'Y') + 'Offset']
:
hasScroll ?
each(this, function(el) {
el[method] = val;
}) :
each(this, function(el) {
if (index) {
el.scrollTo(val, el.scrollY);
} else {
el.scrollTo(el.scrollX, val);
}
});
}
});
/**
* @ignore
* @file node-create
* @author 莫争 <gaoli.gl@taobao.com>
*/
var RE_TAG = /<([\w:]+)/,
RE_XHTML_TAG = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
RE_SINGLE_TAG = /^<(\w+)\s*\/?>(?:<\/\1>)?$/;
var div = doc.createElement('div'),
table = doc.createElement('table'),
tableBody = doc.createElement('tbody'),
tableRow = doc.createElement('tr'),
containers = {
'*' : div,
thead: table,
tbody: table,
tfoot: table,
tr : tableBody,
th : tableRow,
td : tableRow
};
mix(node, {
.create()
.create(html, props)
创建 dom 节点
S.node.create('<div>')
S.node.create('<div />')
S.node.create('<div></div>') //=> 创建 DIV 节点
S.node.create('<div></div>', {
text: 'ok',
css : {color: 'red'}
}); //=> 创建 DIV 节点,内容为'ok',颜色为红色
创建节点的建议直接使用S.Node()
方法
// 创建span节点,并挂在body上
S.Node('<span>hello</span>').appendTo(document.body);
create: function(html, props) {
var key,
ret = [],
tag,
container;
if (!html || !isString(html)) {
return ret;
}
if (RE_SINGLE_TAG.test(html)) {
ret = $(doc.createElement(RegExp.$1));
} else {
html = html.replace(RE_XHTML_TAG, '<$1></$2>');
tag = RE_TAG.test(html) && RegExp.$1;
container = containers[tag] || containers['*'];
container.innerHTML = html;
ret = each(slice.call(container.childNodes), function(el) {
container.removeChild(el);
});
}
if (isPlainObject(props)) {
for (key in props) {
ret.attr(key, props[key]);
}
}
return ret;
},
.html()
.html()
获取符合选择器的第一个元素的 innerHTML
.html(html[, loadScripts])
给符合选择器的所有元素设置 innerHTML 值
loadScripts 表示是否执行 html 中的内嵌脚本,默认 false
var el = S.node.create('<div id="J_check"></div>');
var html = [
'<h3>This is the added part</h3>',
'<script>alert(1)</script>'
].join('');
el.html(html);
//=> 不会 alert(1)
el.html();
//=> <h3>This is the added part</h3>
el.html(html, true);
//=> alert(1)
html: function(html, loadScripts) {
return html === undefined ?
this.length ? this[0].innerHTML : null
:
each(this, function(el) {
$(el).empty().append(html, loadScripts);
});
},
remove: function() {
var self = this;
移除所有事件绑定
self.detach && self.detach();
return each(self, function(el) {
el.parentNode && el.parentNode.removeChild(el)
});
},
empty: function() {
return each(this, function(el) {
el.innerHTML = '';
});
},
clone: function(deep) {
return $(
map(this, function(el) {
return el.cloneNode(!!deep);
})
);
}
});
/**
* @ignore
* @file ie
* @author 莫争 <gaoli.gl@taobao.com>
*/
IE10 及以下浏览器不支持 __proto__
继承,需重写 .node()
和 .isNode()
方法来兼容
if (!('__proto__' in {})) {
mix($, {
node: function(els) {
els = els || [];
mix(els, node);
els.__node = true;
return els;
},
isNode: function(obj) {
return isArray(obj) && '__node' in obj;
}
});
}
/**
* @ignore
* @file output
* @author 莫争 <gaoli.gl@taobao.com>
*/
Node 模块提供的快捷方式
mix(S, {
node : node, // 参元类
Node : $, // 构造器
NodeList: $, // 构造器
one : $.one, // 获取 / 创建一个 Node 对象
all : $.all // 获取 / 创建一批 Node 对象
});
mix(S, {
node : node,
Node : $,
NodeList: $,
one : $.one,
all : $.all
});
S.add && S.add('node', function (S) {
return $;
});
}(this, KISSY));