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 $; });
机器瞎学/数据掩埋/模式混淆/人工智障/深度遗忘/神经掉线/计算机幻觉/专注单身二十五年