util.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
  2. function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
  3. function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
  4. function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
  5. var RE_NUM = /[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source;
  6. function getClientPosition(elem) {
  7. var box;
  8. var x;
  9. var y;
  10. var doc = elem.ownerDocument;
  11. var body = doc.body;
  12. var docElem = doc && doc.documentElement; // 根据 GBS 最新数据,A-Grade Browsers 都已支持 getBoundingClientRect 方法,不用再考虑传统的实现方式
  13. box = elem.getBoundingClientRect(); // 注:jQuery 还考虑减去 docElem.clientLeft/clientTop
  14. // 但测试发现,这样反而会导致当 html 和 body 有边距/边框样式时,获取的值不正确
  15. // 此外,ie6 会忽略 html 的 margin 值,幸运地是没有谁会去设置 html 的 margin
  16. x = box.left;
  17. y = box.top; // In IE, most of the time, 2 extra pixels are added to the top and left
  18. // due to the implicit 2-pixel inset border. In IE6/7 quirks mode and
  19. // IE6 standards mode, this border can be overridden by setting the
  20. // document element's border to zero -- thus, we cannot rely on the
  21. // offset always being 2 pixels.
  22. // In quirks mode, the offset can be determined by querying the body's
  23. // clientLeft/clientTop, but in standards mode, it is found by querying
  24. // the document element's clientLeft/clientTop. Since we already called
  25. // getClientBoundingRect we have already forced a reflow, so it is not
  26. // too expensive just to query them all.
  27. // ie 下应该减去窗口的边框吧,毕竟默认 absolute 都是相对窗口定位的
  28. // 窗口边框标准是设 documentElement ,quirks 时设置 body
  29. // 最好禁止在 body 和 html 上边框 ,但 ie < 9 html 默认有 2px ,减去
  30. // 但是非 ie 不可能设置窗口边框,body html 也不是窗口 ,ie 可以通过 html,body 设置
  31. // 标准 ie 下 docElem.clientTop 就是 border-top
  32. // ie7 html 即窗口边框改变不了。永远为 2
  33. // 但标准 firefox/chrome/ie9 下 docElem.clientTop 是窗口边框,即使设了 border-top 也为 0
  34. x -= docElem.clientLeft || body.clientLeft || 0;
  35. y -= docElem.clientTop || body.clientTop || 0;
  36. return {
  37. left: x,
  38. top: y
  39. };
  40. }
  41. function getScroll(w, top) {
  42. var ret = w["page".concat(top ? 'Y' : 'X', "Offset")];
  43. var method = "scroll".concat(top ? 'Top' : 'Left');
  44. if (typeof ret !== 'number') {
  45. var d = w.document; // ie6,7,8 standard mode
  46. ret = d.documentElement[method];
  47. if (typeof ret !== 'number') {
  48. // quirks mode
  49. ret = d.body[method];
  50. }
  51. }
  52. return ret;
  53. }
  54. function getScrollLeft(w) {
  55. return getScroll(w);
  56. }
  57. function getScrollTop(w) {
  58. return getScroll(w, true);
  59. }
  60. function getOffset(el) {
  61. var pos = getClientPosition(el);
  62. var doc = el.ownerDocument;
  63. var w = doc.defaultView || doc.parentWindow;
  64. pos.left += getScrollLeft(w);
  65. pos.top += getScrollTop(w);
  66. return pos;
  67. }
  68. function _getComputedStyle(elem, name, computedStyle_) {
  69. var val = '';
  70. var d = elem.ownerDocument;
  71. var computedStyle = computedStyle_ || d.defaultView.getComputedStyle(elem, null); // https://github.com/kissyteam/kissy/issues/61
  72. if (computedStyle) {
  73. val = computedStyle.getPropertyValue(name) || computedStyle[name];
  74. }
  75. return val;
  76. }
  77. var _RE_NUM_NO_PX = new RegExp("^(".concat(RE_NUM, ")(?!px)[a-z%]+$"), 'i');
  78. var RE_POS = /^(top|right|bottom|left)$/;
  79. var CURRENT_STYLE = 'currentStyle';
  80. var RUNTIME_STYLE = 'runtimeStyle';
  81. var LEFT = 'left';
  82. var PX = 'px';
  83. function _getComputedStyleIE(elem, name) {
  84. // currentStyle maybe null
  85. // http://msdn.microsoft.com/en-us/library/ms535231.aspx
  86. var ret = elem[CURRENT_STYLE] && elem[CURRENT_STYLE][name]; // 当 width/height 设置为百分比时,通过 pixelLeft 方式转换的 width/height 值
  87. // 一开始就处理了! CUSTOM_STYLE.height,CUSTOM_STYLE.width ,cssHook 解决@2011-08-19
  88. // 在 ie 下不对,需要直接用 offset 方式
  89. // borderWidth 等值也有问题,但考虑到 borderWidth 设为百分比的概率很小,这里就不考虑了
  90. // From the awesome hack by Dean Edwards
  91. // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
  92. // If we're not dealing with a regular pixel number
  93. // but a number that has a weird ending, we need to convert it to pixels
  94. // exclude left right for relativity
  95. if (_RE_NUM_NO_PX.test(ret) && !RE_POS.test(name)) {
  96. // Remember the original values
  97. var style = elem.style;
  98. var left = style[LEFT];
  99. var rsLeft = elem[RUNTIME_STYLE][LEFT]; // prevent flashing of content
  100. elem[RUNTIME_STYLE][LEFT] = elem[CURRENT_STYLE][LEFT]; // Put in the new values to get a computed value out
  101. style[LEFT] = name === 'fontSize' ? '1em' : ret || 0;
  102. ret = style.pixelLeft + PX; // Revert the changed values
  103. style[LEFT] = left;
  104. elem[RUNTIME_STYLE][LEFT] = rsLeft;
  105. }
  106. return ret === '' ? 'auto' : ret;
  107. }
  108. var getComputedStyleX;
  109. if (typeof window !== 'undefined') {
  110. getComputedStyleX = window.getComputedStyle ? _getComputedStyle : _getComputedStyleIE;
  111. }
  112. function each(arr, fn) {
  113. for (var i = 0; i < arr.length; i++) {
  114. fn(arr[i]);
  115. }
  116. }
  117. function isBorderBoxFn(elem) {
  118. return getComputedStyleX(elem, 'boxSizing') === 'border-box';
  119. }
  120. var BOX_MODELS = ['margin', 'border', 'padding'];
  121. var CONTENT_INDEX = -1;
  122. var PADDING_INDEX = 2;
  123. var BORDER_INDEX = 1;
  124. var MARGIN_INDEX = 0;
  125. function swap(elem, options, callback) {
  126. var old = {};
  127. var style = elem.style;
  128. var name; // Remember the old values, and insert the new ones
  129. for (name in options) {
  130. if (options.hasOwnProperty(name)) {
  131. old[name] = style[name];
  132. style[name] = options[name];
  133. }
  134. }
  135. callback.call(elem); // Revert the old values
  136. for (name in options) {
  137. if (options.hasOwnProperty(name)) {
  138. style[name] = old[name];
  139. }
  140. }
  141. }
  142. function getPBMWidth(elem, props, which) {
  143. var value = 0;
  144. var prop;
  145. var j;
  146. var i;
  147. for (j = 0; j < props.length; j++) {
  148. prop = props[j];
  149. if (prop) {
  150. for (i = 0; i < which.length; i++) {
  151. var cssProp = void 0;
  152. if (prop === 'border') {
  153. cssProp = "".concat(prop + which[i], "Width");
  154. } else {
  155. cssProp = prop + which[i];
  156. }
  157. value += parseFloat(getComputedStyleX(elem, cssProp)) || 0;
  158. }
  159. }
  160. }
  161. return value;
  162. }
  163. /**
  164. * A crude way of determining if an object is a window
  165. * @member util
  166. */
  167. function isWindow(obj) {
  168. // must use == for ie8
  169. /* eslint eqeqeq:0 */
  170. return obj != null && obj == obj.window;
  171. }
  172. var domUtils = {};
  173. each(['Width', 'Height'], function (name) {
  174. domUtils["doc".concat(name)] = function (refWin) {
  175. var d = refWin.document;
  176. return Math.max( // firefox chrome documentElement.scrollHeight< body.scrollHeight
  177. // ie standard mode : documentElement.scrollHeight> body.scrollHeight
  178. d.documentElement["scroll".concat(name)], // quirks : documentElement.scrollHeight 最大等于可视窗口多一点?
  179. d.body["scroll".concat(name)], domUtils["viewport".concat(name)](d));
  180. };
  181. domUtils["viewport".concat(name)] = function (win) {
  182. // pc browser includes scrollbar in window.innerWidth
  183. var prop = "client".concat(name);
  184. var doc = win.document;
  185. var body = doc.body;
  186. var documentElement = doc.documentElement;
  187. var documentElementProp = documentElement[prop]; // 标准模式取 documentElement
  188. // backcompat 取 body
  189. return doc.compatMode === 'CSS1Compat' && documentElementProp || body && body[prop] || documentElementProp;
  190. };
  191. });
  192. /*
  193. 得到元素的大小信息
  194. @param elem
  195. @param name
  196. @param {String} [extra] 'padding' : (css width) + padding
  197. 'border' : (css width) + padding + border
  198. 'margin' : (css width) + padding + border + margin
  199. */
  200. function getWH(elem, name, extra) {
  201. if (isWindow(elem)) {
  202. return name === 'width' ? domUtils.viewportWidth(elem) : domUtils.viewportHeight(elem);
  203. } else if (elem.nodeType === 9) {
  204. return name === 'width' ? domUtils.docWidth(elem) : domUtils.docHeight(elem);
  205. }
  206. var which = name === 'width' ? ['Left', 'Right'] : ['Top', 'Bottom'];
  207. var borderBoxValue = name === 'width' ? elem.offsetWidth : elem.offsetHeight;
  208. var computedStyle = getComputedStyleX(elem);
  209. var isBorderBox = isBorderBoxFn(elem, computedStyle);
  210. var cssBoxValue = 0;
  211. if (borderBoxValue == null || borderBoxValue <= 0) {
  212. borderBoxValue = undefined; // Fall back to computed then un computed css if necessary
  213. cssBoxValue = getComputedStyleX(elem, name);
  214. if (cssBoxValue == null || Number(cssBoxValue) < 0) {
  215. cssBoxValue = elem.style[name] || 0;
  216. } // Normalize '', auto, and prepare for extra
  217. cssBoxValue = parseFloat(cssBoxValue) || 0;
  218. }
  219. if (extra === undefined) {
  220. extra = isBorderBox ? BORDER_INDEX : CONTENT_INDEX;
  221. }
  222. var borderBoxValueOrIsBorderBox = borderBoxValue !== undefined || isBorderBox;
  223. var val = borderBoxValue || cssBoxValue;
  224. if (extra === CONTENT_INDEX) {
  225. if (borderBoxValueOrIsBorderBox) {
  226. return val - getPBMWidth(elem, ['border', 'padding'], which, computedStyle);
  227. }
  228. return cssBoxValue;
  229. }
  230. if (borderBoxValueOrIsBorderBox) {
  231. var padding = extra === PADDING_INDEX ? -getPBMWidth(elem, ['border'], which, computedStyle) : getPBMWidth(elem, ['margin'], which, computedStyle);
  232. return val + (extra === BORDER_INDEX ? 0 : padding);
  233. }
  234. return cssBoxValue + getPBMWidth(elem, BOX_MODELS.slice(extra), which, computedStyle);
  235. }
  236. var cssShow = {
  237. position: 'absolute',
  238. visibility: 'hidden',
  239. display: 'block'
  240. }; // fix #119 : https://github.com/kissyteam/kissy/issues/119
  241. function getWHIgnoreDisplay(elem) {
  242. var val;
  243. var args = arguments; // in case elem is window
  244. // elem.offsetWidth === undefined
  245. if (elem.offsetWidth !== 0) {
  246. val = getWH.apply(undefined, args);
  247. } else {
  248. swap(elem, cssShow, function () {
  249. val = getWH.apply(undefined, args);
  250. });
  251. }
  252. return val;
  253. }
  254. function css(el, name, v) {
  255. var value = v;
  256. if (_typeof(name) === 'object') {
  257. for (var i in name) {
  258. if (name.hasOwnProperty(i)) {
  259. css(el, i, name[i]);
  260. }
  261. }
  262. return undefined;
  263. }
  264. if (typeof value !== 'undefined') {
  265. if (typeof value === 'number') {
  266. value += 'px';
  267. }
  268. el.style[name] = value;
  269. return undefined;
  270. }
  271. return getComputedStyleX(el, name);
  272. }
  273. each(['width', 'height'], function (name) {
  274. var first = name.charAt(0).toUpperCase() + name.slice(1);
  275. domUtils["outer".concat(first)] = function (el, includeMargin) {
  276. return el && getWHIgnoreDisplay(el, name, includeMargin ? MARGIN_INDEX : BORDER_INDEX);
  277. };
  278. var which = name === 'width' ? ['Left', 'Right'] : ['Top', 'Bottom'];
  279. domUtils[name] = function (elem, val) {
  280. if (val !== undefined) {
  281. if (elem) {
  282. var computedStyle = getComputedStyleX(elem);
  283. var isBorderBox = isBorderBoxFn(elem);
  284. if (isBorderBox) {
  285. val += getPBMWidth(elem, ['padding', 'border'], which, computedStyle);
  286. }
  287. return css(elem, name, val);
  288. }
  289. return undefined;
  290. }
  291. return elem && getWHIgnoreDisplay(elem, name, CONTENT_INDEX);
  292. };
  293. }); // 设置 elem 相对 elem.ownerDocument 的坐标
  294. function setOffset(elem, offset) {
  295. // set position first, in-case top/left are set even on static elem
  296. if (css(elem, 'position') === 'static') {
  297. elem.style.position = 'relative';
  298. }
  299. var old = getOffset(elem);
  300. var ret = {};
  301. var current;
  302. var key;
  303. for (key in offset) {
  304. if (offset.hasOwnProperty(key)) {
  305. current = parseFloat(css(elem, key)) || 0;
  306. ret[key] = current + offset[key] - old[key];
  307. }
  308. }
  309. css(elem, ret);
  310. }
  311. export default _objectSpread({
  312. getWindow: function getWindow(node) {
  313. var doc = node.ownerDocument || node;
  314. return doc.defaultView || doc.parentWindow;
  315. },
  316. offset: function offset(el, value) {
  317. if (typeof value !== 'undefined') {
  318. setOffset(el, value);
  319. } else {
  320. return getOffset(el);
  321. }
  322. },
  323. isWindow: isWindow,
  324. each: each,
  325. css: css,
  326. clone: function clone(obj) {
  327. var ret = {};
  328. for (var i in obj) {
  329. if (obj.hasOwnProperty(i)) {
  330. ret[i] = obj[i];
  331. }
  332. }
  333. var overflow = obj.overflow;
  334. if (overflow) {
  335. for (var _i in obj) {
  336. if (obj.hasOwnProperty(_i)) {
  337. ret.overflow[_i] = obj.overflow[_i];
  338. }
  339. }
  340. }
  341. return ret;
  342. },
  343. scrollLeft: function scrollLeft(w, v) {
  344. if (isWindow(w)) {
  345. if (v === undefined) {
  346. return getScrollLeft(w);
  347. }
  348. window.scrollTo(v, getScrollTop(w));
  349. } else {
  350. if (v === undefined) {
  351. return w.scrollLeft;
  352. }
  353. w.scrollLeft = v;
  354. }
  355. },
  356. scrollTop: function scrollTop(w, v) {
  357. if (isWindow(w)) {
  358. if (v === undefined) {
  359. return getScrollTop(w);
  360. }
  361. window.scrollTo(getScrollLeft(w), v);
  362. } else {
  363. if (v === undefined) {
  364. return w.scrollTop;
  365. }
  366. w.scrollTop = v;
  367. }
  368. },
  369. viewportWidth: 0,
  370. viewportHeight: 0
  371. }, domUtils);