mass Framework event模块 v5
主要改动以下:
- css_fix去掉对auto的处理
- 为了提高性能,内部使用getter, getStyle进行快速取样式精确值
- 利用css3 calc函数进行增量或减量的样式设置
- 为cssNumber添加两个新成员
- 尝式使用Shadow DOM获取默认样式值
css.js
define("css", top.getComputedStyle ? ["$node"] : ["$css_fix"], function($) {
var adapter = $.cssHooks || ($.cssHooks = {}),
rrelNum = /^([\-+])=([\-+.\de]+)/,
rnumnonpx = /^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,
cssTransform = $.cssName("transform");
//这里的属性不需要自行添加px
$.cssNumber = $.oneObject("columnCount,fillOpacity,fontSizeAdjust,fontWeight,lineHeight,opacity,orphans,widows,zIndex,zoom,rotate");
//有关单位转换的 http://heygrady.com/blog/2011/12/21/length-and-angle-unit-conversion-in-javascript/
if(window.getComputedStyle) {
$.getStyles = function(node) {
return window.getComputedStyle(node, null);
}
adapter["_default:get"] = function(node, name, styles) {
var ret, width, minWidth, maxWidth
styles = styles || getStyles(node);
if(styles) {
ret = name == "filter" ? styles.getPropertyValue(name) : styles[name]
var style = node.style; //这里只有firefox与IE10会智能处理未插入DOM树的节点的样式,它会自动打内联样式
if(ret === "" && !$.contains(node.ownerDocument, node)) {
ret = style[name]; //其他需要我们手动取内联样式
}
// Dean Edwards大神的hack,用于转换margin的百分比值为更有用的像素值
// webkit不能转换top, bottom, left, right, margin, text-indent的百分比值
if(/^margin/.test(name) && rnumnonpx.test(ret)) {
width = style.width;
minWidth = style.minWidth;
maxWidth = style.maxWidth;
style.minWidth = style.maxWidth = style.width = ret;
ret = styles.width;
style.width = width;
style.minWidth = minWidth;
style.maxWidth = maxWidth;
}
}
return ret;
}
}
var getStyles = $.getStyles;
delete $.getStyles;
//用于性能优化,内部不用转换单位,属性名风格及进行相对赋值,远比调用$.css高效
var getter = adapter["_default:get"];
function parseNumber(styles, name) {
return parseFloat(styles[name]) || 0;
}
adapter["zIndex:get"] = function(node) {
while(node.nodeType !== 9) {
//即使元素定位了,但如果zindex设置为"aaa"这样的无效值,浏览器都会返回auto;
//如果没有指定zindex值,IE会返回数字0,其他返回auto
var position = getter(node, "position") || "static";
if(position !== "static") {
// <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
var value = parseInt(getter(node, "zIndex"), 10);
if(!isNaN(value) && value !== 0) {
return value;
}
}
node = node.parentNode;
}
return 0;
}
adapter["_default:set"] = function(node, name, value) {
node.style[name] = value;
}
// 获取CSS3变形中的角度
adapter["rotate:get"] = function(node) {
return $._data(node, 'rotate') || 0;
}
if(cssTransform) {
adapter["rotate:set"] = function(node, name, value) {
$._data(node, 'rotate', value);
node.style[cssTransform] = 'rotate(' + (value * Math.PI / 180) + 'rad)';
}
}
var supportBoxSizing = $.cssName("box-sizing");
adapter["boxSizing:get"] = function(node, name) {
return supportBoxSizing ? getter(node, name) : document.compatMode == "BackCompat" ? "border-box" : "content-box"
}
$.css = function(node, name, value, styles) {
if(node.style) { //注意string经过call之后,变成String伪对象,不能简单用typeof来检测
var prop = $.String.camelize(name)
name = $.cssName(name);
styles = styles || getStyles(node);
if(value === void 0) { //获取样式
return(adapter[prop + ":get"] || getter)(node, name, styles);
} else { //设置样式
var type = typeof value,
temp;
if(type === "string" && (temp = rrelNum.exec(value))) {
if($.support.calc && name in styles) {
//在firefox18, ie10中必须要求运算符两边都有空白才生效
var cur = styles[name],
unit = (cur.match(/[a-z%]+$/) || [""])[0];
return node.style[name] = $.support.calc + "(" + [styles[name], temp[1], temp[2] + unit].join(" ") + ")";
} else {
value = (+(temp[1] + 1) * +temp[2]) + parseFloat($.css(node, name, void 0, styles));
type = "number";
}
}
if(type === "number" && !isFinite(value + "")) { //因为isFinite(null) == true
return;
}
if(type === "number" && !$.cssNumber[prop]) {
value += "px";
}
if(value === "" && !$.support.cloneBackgroundStyle && name.indexOf("background") === 0) {
node.style[name] = "inherit";
}
(adapter[prop + ":set"] || adapter["_default:set"])(node, name, value, styles);
}
}
}
$.fn.css = function(name, value) {
return $.access(this, name, value, $.css);
}
var cssPair = {
Width: ['Left', 'Right'],
Height: ['Top', 'Bottom']
}
var cssShow = {
position: "absolute",
visibility: "hidden",
display: "block"
}
function showHidden(node, array) {
//http://www.cnblogs.com/rubylouvre/archive/2012/10/27/2742529.html
if(node && node.nodeType == 1 && node.offsetWidth == 0) {
if(getter(node, "display") == "none") {
var obj = {
node: node
}
for(var name in cssShow) {
obj[name] = node.style[name];
node.style[name] = cssShow[name];
}
array.push(obj);
}
showHidden(node.parentNode, array);
}
}
function setWH(node, name, val, extra) {
var which = cssPair[name],
styles = getStyles(node);
which.forEach(function(direction) {
if(extra < 1) val -= parseNumber(styles, 'padding' + direction);
if(extra < 2) val -= parseNumber(styles, 'border' + direction + 'Width');
if(extra === 3) {
val += parseFloat(getter(node, 'margin' + direction, styles)) || 0;
}
if(extra === "padding-box") {
val += parseNumber(styles, 'padding' + direction);
}
if(extra === "border-box") {
val += parseNumber(styles, 'padding' + direction);
val += parseNumber(styles, 'border' + direction + 'Width');
}
});
return val
}
function getWH(node, name, extra) { //注意 name是首字母大写
var hidden = [];
showHidden(node, hidden);
var val = setWH(node, name, node["offset" + name], extra);
for(var i = 0, obj; obj = hidden[i++];) {
node = obj.node;
for(name in obj) {
if(typeof obj[name] == "string") {
node.style[name] = obj[name];
}
}
}
return val;
}
//========================= 处理 width, height, innerWidth, innerHeight, outerWidth, outerHeight ========
"Height,Width".replace($.rword, function(name) {
var lower = name.toLowerCase(),
clientProp = "client" + name,
scrollProp = "scroll" + name,
offsetProp = "offset" + name;
$.cssHooks[lower + ":get"] = function(node) {
return getWH(node, name, 0) + "px"; //添加相应适配器
}
$.cssHooks[lower + ":set"] = function(node, nick, value) {
var box = $.css(node, "box-sizing"); //nick防止与外面name冲突
node.style[nick] = box == "content-box" ? value : setWH(node, name, parseFloat(value), box) + "px";
}
"inner_1,b_0,outer_2".replace($.rmapper, function(a, b, num) {
var method = b == "b" ? lower : b + name;
$.fn[method] = function(value) {
num = b == "outer" && value === true ? 3 : num;
return $.access(this, num, value, function(node, num, size) {
if($.type(node, "Window")) { //取得窗口尺寸,IE9后可以用node.innerWidth /innerHeight代替
return node["inner" + name] || node.document.documentElement[clientProp];
}
if(node.nodeType === 9) { //取得页面尺寸
var doc = node.documentElement;
//FF chrome html.scrollHeight< body.scrollHeight
//IE 标准模式 : html.scrollHeight> body.scrollHeight
//IE 怪异模式 : html.scrollHeight 最大等于可视窗口多一点?
return Math.max(
node.body[scrollProp], doc[scrollProp], node.body[offsetProp], doc[offsetProp], doc[clientProp]);
} else if(size === void 0) {
return getWH(node, name, num)
} else {
return num > 0 ? this : $.css(node, lower, size);
}
}, this)
}
})
});
//========================= 生成 show hide toggle =========================
var cacheDisplay = $.oneObject("a,abbr,b,span,strong,em,font,i,img,kbd", "inline"),
blocks = $.oneObject("div,h1,h2,h3,h4,h5,h6,section,p", "block"),
sandbox, sandboxDoc
shadowRoot, shadowDoc, shadowBody, shadowWin, reuse
$.applyShadowDOM = function(callback) {
//用于提供一个沙箱环境,IE6-10,opera,safari,firefox使用iframe, chrome20+使用Shodow DOM
if(!shadowRoot) {
if(window.WebKitShadowRoot) { //如果支持WebKitShadowRoot
shadowRoot = new WebKitShadowRoot($.html);
shadowBody = document.createElement("div");
shadowBody.style.cssText = "width:0px;height:0px;"
shadowRoot.appendChild(shadowBody);
} else {
shadowRoot = document.createElement("iframe");
shadowRoot.frameBorder = shadowRoot.width = shadowRoot.height = 0;
}
}
if(shadowRoot.nodeType == 1) {
$.html.appendChild(shadowRoot);
if(!reuse) { //firefox, safari, chrome不能重用shadowDoc,shadowWin
shadowDoc = shadowRoot.contentDocument || shadowRoot.contentWindow.document;
shadowWin = shadowDoc.defaultView || shadowDoc.parentWindow;
shadowDoc.write("<!doctype html><html><body>");
shadowDoc.close();
reuse = window.VBArray || window.opera; //opera9-12, ie6-10有效
}
callback(shadowWin, shadowDoc, shadowDoc.body);
$.html.removeChild(shadowRoot);
} else {
callback(window, document, shadowBody);
shadowBody.innerHTML = "";
}
}
$.mix(cacheDisplay, blocks);
$.parseDisplay = function(nodeName) {
nodeName = nodeName.toLowerCase();
if(!cacheDisplay[nodeName]) {
$.applyShadowDOM(function(win, doc, body) {
var node = doc.createElement(nodeName),
val
body.appendChild(node);
if(win.getComputedStyle) {
val = win.getComputedStyle(node, null).display
} else {
val = node.currentStyle.display;
}
cacheDisplay[nodeName] = val;
});
}
return cacheDisplay[nodeName];
}
function isHidden(node) {
return node.sourceIndex === 0 || getter(node, "display") === "none" || !$.contains(node.ownerDocument, node);
}
$._isHidden = isHidden;
function toggelDisplay(nodes, show) {
var elem, values = [],
status = [],
index = 0,
length = nodes.length;
//由于传入的元素们可能存在包含关系,因此分开两个循环来处理,第一个循环用于取得当前值或默认值
for(; index < length; index++) {
elem = nodes[index];
if(!elem.style) {
continue;
}
values[index] = $._data(elem, "olddisplay");
status[index] = isHidden(elem)
if(!values[index]) {
values[index] = status[index] ? $.parseDisplay(elem.nodeName) : getter(elem, "display");
$._data(elem, "olddisplay", values[index])
}
}
//第二个循环用于设置样式,-1为toggle, 1为show, 0为hide
for(index = 0; index < length; index++) {
elem = nodes[index];
if(!elem.style) {
continue;
}
show = show === -1 ? !status[index] : show;
elem.style.display = show ? values[index] : "none";
}
return nodes;
}
$.fn.show = function() {
return toggelDisplay(this, 1);
}
$.fn.hide = function() {
return toggelDisplay(this, 0);
}
//state为true时,强制全部显示,为false,强制全部隐藏
$.fn.toggle = function(state) {
return toggelDisplay(this, typeof state == "boolean" ? state : -1);
}
//========================= 处理 offset =========================
function setOffset(node, options) {
if(node && node.nodeType == 1) {
var position = getter(node, "position");
//强逼定位
if(position === "static") {
node.style.position = "relative";
}
var curElem = $(node),
curOffset = curElem.offset(),
curCSSTop = getter(node, "top"),
curCSSLeft = getter(node, "left"),
calculatePosition = (position === "absolute" || position === "fixed") && [curCSSTop, curCSSLeft].indexOf("auto") > -1,
props = {},
curPosition = {},
curTop, curLeft;
if(calculatePosition) {
curPosition = curElem.position();
curTop = curPosition.top;
curLeft = curPosition.left;
} else {
//如果是相对定位只要用当前top,left做基数
curTop = parseFloat(curCSSTop) || 0;
curLeft = parseFloat(curCSSLeft) || 0;
}
if(options.top != null) {
props.top = (options.top - curOffset.top) + curTop;
}
if(options.left != null) {
props.left = (options.left - curOffset.left) + curLeft;
}
curElem.css(props);
}
}
$.fn.offset = function(options) { //取得第一个元素位于页面的坐标
if(arguments.length) {
return(!options || (!isFinite(options.top) && !isFinite(options.left))) ? this : this.each(function() {
setOffset(this, options);
});
}
var node = this[0],
doc = node && node.ownerDocument,
pos = {
left: 0,
top: 0
};
if(!doc) {
return pos;
}
//http://hkom.blog1.fc2.com/?mode=m&no=750 body的偏移量是不包含margin的
//我们可以通过getBoundingClientRect来获得元素相对于client的rect.
//http://msdn.microsoft.com/en-us/library/ms536433.aspx
var box = node.getBoundingClientRect(),
win = getWindow(doc),
root = (navigator.vendor || doc.compatMode == "BackCompat") ? doc.body : doc.documentElement,
clientTop = root.clientTop >> 0,
clientLeft = root.clientLeft >> 0,
scrollTop = win.pageYOffset || root.scrollTop,
scrollLeft = win.pageXOffset || root.scrollLeft;
// 把滚动距离加到left,top中去。
// IE一些版本中会自动为HTML元素加上2px的border,我们需要去掉它
// http://msdn.microsoft.com/en-us/library/ms533564(VS.85).aspx
pos.top = box.top + scrollTop - clientTop, pos.left = box.left + scrollLeft - clientLeft;
return pos;
}
//========================= 处理 position =========================
$.fn.position = function() { //取得元素相对于其offsetParent的坐标
var offset, node = this[0],
parentOffset = { //默认的offsetParent相对于视窗的距离
top: 0,
left: 0
}
if(!node || node.nodeType !== 1) {
return
}
//fixed 元素是相对于window
if(getter(node, "position") === "fixed") {
offset = node.getBoundingClientRect();
} else {
offset = this.offset(); //得到元素相对于视窗的距离(我们只有它的top与left)
var offsetParent = this.offsetParent();
if(offsetParent[0].tagName !== "HTML") {
parentOffset = offsetParent.offset(); //得到它的offsetParent相对于视窗的距离
}
var styles = getStyles(offsetParent[0]);
parentOffset.top += parseNumber(styles, "borderTopWidth");
parentOffset.left += parseNumber(styles, "borderLeftWidth");
}
return {
top: offset.top - parentOffset.top - parseFloat(getter(node, "marginTop", styles)) || 0,
left: offset.left - parentOffset.left - parseFloat(getter(node, "marginLeft", styles)) || 0
};
}
//https://github.com/beviz/jquery-caret-position-getter/blob/master/jquery.caretposition.js
//https://developer.mozilla.org/en-US/docs/DOM/element.offsetParent
//如果元素被移出DOM树,或display为none,或作为HTML或BODY元素,或其position的精确值为fixed时,返回null
$.fn.offsetParent = function() {
return this.map(function() {
var el = this.offsetParent;
while(el && (el.parentNode.nodeType !== 9) && getter(el, "position") === "static") {
el = el.offsetParent;
}
return el || document.documentElement;
});
}
$.fn.scrollParent = function() {
var scrollParent, node = this[0],
pos = getter(node, "position")
if((window.VBArray && (/(static|relative)/).test(pos)) || (/absolute/).test(pos)) {
scrollParent = this.parents().filter(function() {
return(/(relative|absolute|fixed)/).test(getter(this, "position")) && (/(auto|scroll)/).test(getter(this, "overflow") + $.css(this, "overflow-y") + $.css(this, "overflow-x"));
}).eq(0);
} else {
scrollParent = this.parents().filter(function() {
return(/(auto|scroll)/).test(getter(this, "overflow") + $.css(this, "overflow-y") + $.css(this, "overflow-x"));
}).eq(0);
}
return(/fixed/).test(pos) || !scrollParent.length ? $(document) : scrollParent;
}
//========================= 处理 scrollLeft scrollTop =========================
"scrollLeft_pageXOffset,scrollTop_pageYOffset".replace($.rmapper, function(_, method, prop) {
$.fn[method] = function(val) {
var node, win, top = method == "scrollTop";
if(val === void 0) {
node = this[0];
if(!node) {
return null;
}
win = getWindow(node); //获取第一个元素的scrollTop/scrollLeft
return win ? (prop in win) ? win[prop] : win.document.documentElement[method] : node[method];
}
return this.each(function() { //设置匹配元素的scrollTop/scrollLeft
win = getWindow(this);
if(win) {
win.scrollTo(!top ? val : $(win).scrollLeft(), top ? val : $(win).scrollTop());
} else {
this[method] = val;
}
});
};
});
function getWindow(node) {
return $.type(node, "Window") ? node : node.nodeType === 9 ? node.defaultView || node.parentWindow : false;
}
return $;
});
css_fix.js
define("css_fix", !! top.getComputedStyle, ["$node"], function($) {
var adapter = $.cssHooks = {},
ie8 = !! top.XDomainRequest,
rfilters = /[\w\:\.]+\([^)]+\)/g,
rnumnonpx = /^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,
rposition = /^(top|right|bottom|left)$/,
salpha = "DXImageTransform.Microsoft.Alpha",
border = {
thin: ie8 ? '1px' : '2px',
medium: ie8 ? '3px' : '4px',
thick: ie8 ? '5px' : '6px'
};
$.getStyles = function(node) {
return node.currentStyle;
}
adapter["_default:get"] = function(node, name, styles) {
//取得精确值,不过它有可能是带em,pc,mm,pt,%等单位
var currentStyle = styles || node.currentStyle;
var ret = currentStyle[name];
if((rnumnonpx.test(ret) && !rposition.test(ret))) {
//①,保存原有的style.left, runtimeStyle.left,
var style = node.style,
left = style.left,
rsLeft = node.runtimeStyle.left;
//②由于③处的style.left = xxx会影响到currentStyle.left,
//因此把它currentStyle.left放到runtimeStyle.left,
//runtimeStyle.left拥有最高优先级,不会style.left影响
node.runtimeStyle.left = currentStyle.left;
//③将精确值赋给到style.left,然后通过IE的另一个私有属性 style.pixelLeft
//得到单位为px的结果;fontSize的分支见http://bugs.jquery.com/ticket/760
style.left = name === 'fontSize' ? '1em' : (ret || 0);
ret = style.pixelLeft + "px";
//④还原 style.left,runtimeStyle.left
style.left = left;
node.runtimeStyle.left = rsLeft;
}
if(ret == "medium") {
name = name.replace("Width", "Style");
//border width 默认值为medium,即使其为0"
if(currentStyle[name] == "none") {
ret = "0px";
}
}
return ret === "" ? "auto" : border[ret] || ret;
}
//========================= 处理 opacity =========================
adapter["opacity:get"] = function(node) {
//这是最快的获取IE透明值的方式,不需要动用正则了!
var alpha = node.filters.alpha || node.filters[salpha],
op = alpha ? alpha.opacity : 100;
return(op / 100) + ""; //确保返回的是字符串
}
//http://www.freemathhelp.com/matrix-multiplication.html
//金丝楠木是皇家专用木材,一般只有皇帝可以使用做梓宫。
adapter["opacity:set"] = function(node, name, value, currentStyle) {
var style = node.style;
if(!isFinite(value)) { //"xxx" * 100 = NaN
return;
}
value = (value > 0.999) ? 100 : (value < 0.001) ? 0 : value * 100;
if(!currentStyle.hasLayout) style.zoom = 1; //让元素获得hasLayout
var filter = currentStyle.filter || style.filter || "";
//http://snook.ca/archives/html_and_css/ie-position-fixed-opacity-filter
//IE78的透明滤镜当其值为100时会让文本模糊不清
if(value == 100) { //IE78的透明滤镜当其值为100时会让文本模糊不清
// var str = "filter: progid:DXImageTransform.Microsoft.Alpha(opacity=100) Chroma(Color='#FFFFFF')"+
// "progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand',"+
// "M11=1.5320888862379554, M12=-1.2855752193730787, M21=1.2855752193730796, M22=1.5320888862379558)";
value = style.filter = filter.replace(rfilters, function(a) {
return /alpha/i.test(a) ? "" : a; //可能存在多个滤镜,只清掉透明部分
});
//如果只有一个透明滤镜 就直接去掉
if(value.trim() == "" && style.removeAttribute) {
style.removeAttribute("filter");
}
return;
}
//如果已经设置过透明滤镜可以使用以下便捷方式
var alpha = node.filters.alpha || node.filters[salpha];
if(alpha) {
alpha.opacity = value;
} else {
style.filter = ((filter ? filter + "," : "") + "alpha(opacity=" + value + ")");
}
}
/**========================= 处理 user-select =========================
* auto——默认值,用户可以选中元素中的内容
* none——用户不能选择元素中的任何内容
* text——用户可以选择元素中的文本
* element——文本可选,但仅限元素的边界内(只有IE和FF支持)
* all——在编辑器内,如果双击/上下文点击发生在子元素上,改值的最高级祖先元素将被选中。
* -moz-none——firefox私有,元素和子元素的文本将不可选,但是,子元素可以通过text重设回可选。
* https://developer.mozilla.org/en-US/docs/CSS/user-select
*/
adapter["userSelect:set"] = function(node, name, value) {
var allow = /none/.test(value) ? "on" : "",
e, i = 0,
els = node.getElementsByTagName('*');
node.setAttribute('unselectable', allow);
while((e = els[i++])) {
switch(e.tagName) {
case 'IFRAME':
case 'TEXTAREA':
case 'INPUT':
case 'SELECT':
break;
default:
e.setAttribute('unselectable', allow);
}
}
};
//========================= 处理 background-position =========================
adapter["backgroundPosition:get"] = function(node, _,style ) {
return style.backgroundPositionX + " " + style.backgroundPositionX;
};
//========================= 处理 rotate =========================
var stransform = "DXImageTransform.Microsoft.Matrix";
adapter.centerOrigin = "margin";
adapter["rotate:set"] = function(node, name, value) {
$._data(node, 'rotate', value);
var matrix = node.filters[stransform];
if(!matrix) {
node.style.filter += "progid:" + stransform + "(M11=1,M12=1,M21=1,M22=1,sizingMethod='auto expand')";
matrix = node.filters[stransform];
}
var _rad = value * Math.PI / 180,
costheta = Math.cos(_rad),
sintheta = Math.sin(_rad);
matrix.M11 = costheta;
matrix.M12 = -sintheta;
matrix.M21 = sintheta;
matrix.M22 = costheta;
name = adapter.centerOrigin;
node.style[name == 'margin' ? 'marginLeft' : 'left'] = -(node.offsetWidth / 2) + (node.clientWidth / 2) + "px";
node.style[name == 'margin' ? 'marginTop' : 'top'] = -(node.offsetHeight / 2) + (node.clientHeight / 2) + "px";
}
return $;
});
机器瞎学/数据掩埋/模式混淆/人工智障/深度遗忘/神经掉线/计算机幻觉/专注单身二十五年
浙公网安备 33010602011771号