popovers.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. /**
  2. * Popovers
  3. * @param {type} $
  4. * @param {type} window
  5. * @param {type} document
  6. * @param {type} name
  7. * @param {type} undefined
  8. * @returns {undefined}
  9. */
  10. (function($, window, document, name) {
  11. var CLASS_POPOVER = $.className('popover');
  12. var CLASS_POPOVER_ARROW = $.className('popover-arrow');
  13. var CLASS_ACTION_POPOVER = $.className('popover-action');
  14. var CLASS_BACKDROP = $.className('backdrop');
  15. var CLASS_BAR_POPOVER = $.className('bar-popover');
  16. var CLASS_BAR_BACKDROP = $.className('bar-backdrop');
  17. var CLASS_ACTION_BACKDROP = $.className('backdrop-action');
  18. var CLASS_ACTIVE = $.className('active');
  19. var CLASS_BOTTOM = $.className('bottom');
  20. var handle = function(event, target) {
  21. if (target.tagName === 'A' && target.hash) {
  22. $.targets._popover = document.getElementById(target.hash.replace('#', ''));
  23. if ($.targets._popover && $.targets._popover.classList.contains(CLASS_POPOVER)) {
  24. return target;
  25. } else {
  26. $.targets._popover = null;
  27. }
  28. }
  29. return false;
  30. };
  31. $.registerTarget({
  32. name: name,
  33. index: 60,
  34. handle: handle,
  35. target: false,
  36. isReset: false,
  37. isContinue: true
  38. });
  39. var onPopoverShown = function(e) {
  40. this.removeEventListener('webkitTransitionEnd', onPopoverShown);
  41. this.addEventListener($.EVENT_MOVE, $.preventDefault);
  42. $.trigger(this, 'shown', this);
  43. }
  44. var onPopoverHidden = function(e) {
  45. setStyle(this, 'none');
  46. this.removeEventListener('webkitTransitionEnd', onPopoverHidden);
  47. this.removeEventListener($.EVENT_MOVE, $.preventDefault);
  48. $.trigger(this, 'hidden', this);
  49. };
  50. var backdrop = (function() {
  51. var element = document.createElement('div');
  52. element.classList.add(CLASS_BACKDROP);
  53. element.addEventListener($.EVENT_MOVE, $.preventDefault);
  54. element.addEventListener('tap', function(e) {
  55. var popover = $.targets._popover;
  56. if (popover) {
  57. popover.addEventListener('webkitTransitionEnd', onPopoverHidden);
  58. popover.classList.remove(CLASS_ACTIVE);
  59. removeBackdrop(popover);
  60. }
  61. });
  62. return element;
  63. }());
  64. var removeBackdropTimer;
  65. var removeBackdrop = function(popover) {
  66. backdrop.setAttribute('style', 'opacity:0');
  67. $.targets.popover = $.targets._popover = null; //reset
  68. removeBackdropTimer = $.later(function() {
  69. if (!popover.classList.contains(CLASS_ACTIVE) && backdrop.parentNode && backdrop.parentNode === document.body) {
  70. document.body.removeChild(backdrop);
  71. }
  72. }, 350);
  73. };
  74. window.addEventListener('tap', function(e) {
  75. if (!$.targets.popover) {
  76. return;
  77. }
  78. var toggle = false;
  79. var target = e.target;
  80. for (; target && target !== document; target = target.parentNode) {
  81. if (target === $.targets.popover) {
  82. toggle = true;
  83. }
  84. }
  85. if (toggle) {
  86. e.detail.gesture.preventDefault(); //fixed hashchange
  87. togglePopover($.targets._popover, $.targets.popover);
  88. }
  89. });
  90. var togglePopover = function(popover, anchor, state) {
  91. if ((state === 'show' && popover.classList.contains(CLASS_ACTIVE)) || (state === 'hide' && !popover.classList.contains(CLASS_ACTIVE))) {
  92. return;
  93. }
  94. removeBackdropTimer && removeBackdropTimer.cancel(); //取消remove的timer
  95. //remove一遍,以免来回快速切换,导致webkitTransitionEnd不触发,无法remove
  96. popover.removeEventListener('webkitTransitionEnd', onPopoverShown);
  97. popover.removeEventListener('webkitTransitionEnd', onPopoverHidden);
  98. backdrop.classList.remove(CLASS_BAR_BACKDROP);
  99. backdrop.classList.remove(CLASS_ACTION_BACKDROP);
  100. var _popover = document.querySelector($.classSelector('.popover.active'));
  101. if (_popover) {
  102. // _popover.setAttribute('style', '');
  103. _popover.addEventListener('webkitTransitionEnd', onPopoverHidden);
  104. _popover.classList.remove(CLASS_ACTIVE);
  105. // _popover.removeEventListener('webkitTransitionEnd', onPopoverHidden);
  106. //同一个弹出则直接返回,解决同一个popover的toggle
  107. if (popover === _popover) {
  108. removeBackdrop(_popover);
  109. return;
  110. }
  111. }
  112. var isActionSheet = false;
  113. if (popover.classList.contains(CLASS_BAR_POPOVER) || popover.classList.contains(CLASS_ACTION_POPOVER)) { //navBar
  114. if (popover.classList.contains(CLASS_ACTION_POPOVER)) { //action sheet popover
  115. isActionSheet = true;
  116. backdrop.classList.add(CLASS_ACTION_BACKDROP);
  117. } else { //bar popover
  118. backdrop.classList.add(CLASS_BAR_BACKDROP);
  119. // if (anchor) {
  120. // if (anchor.parentNode) {
  121. // var offsetWidth = anchor.offsetWidth;
  122. // var offsetLeft = anchor.offsetLeft;
  123. // var innerWidth = window.innerWidth;
  124. // popover.style.left = (Math.min(Math.max(offsetLeft, defaultPadding), innerWidth - offsetWidth - defaultPadding)) + "px";
  125. // } else {
  126. // //TODO anchor is position:{left,top,bottom,right}
  127. // }
  128. // }
  129. }
  130. }
  131. setStyle(popover, 'block'); //actionsheet transform
  132. popover.offsetHeight;
  133. popover.classList.add(CLASS_ACTIVE);
  134. backdrop.setAttribute('style', '');
  135. document.body.appendChild(backdrop);
  136. calPosition(popover, anchor, isActionSheet); //position
  137. backdrop.classList.add(CLASS_ACTIVE);
  138. popover.addEventListener('webkitTransitionEnd', onPopoverShown);
  139. };
  140. var setStyle = function(popover, display, top, left) {
  141. var style = popover.style;
  142. if (typeof display !== 'undefined')
  143. style.display = display;
  144. if (typeof top !== 'undefined')
  145. style.top = top + 'px';
  146. if (typeof left !== 'undefined')
  147. style.left = left + 'px';
  148. };
  149. var calPosition = function(popover, anchor, isActionSheet) {
  150. if (!popover || !anchor) {
  151. return;
  152. }
  153. if (isActionSheet) { //actionsheet
  154. setStyle(popover, 'block')
  155. return;
  156. }
  157. var wWidth = window.innerWidth;
  158. var wHeight = window.innerHeight;
  159. var pWidth = popover.offsetWidth;
  160. var pHeight = popover.offsetHeight;
  161. var aWidth = anchor.offsetWidth;
  162. var aHeight = anchor.offsetHeight;
  163. var offset = $.offset(anchor);
  164. var arrow = popover.querySelector('.' + CLASS_POPOVER_ARROW);
  165. if (!arrow) {
  166. arrow = document.createElement('div');
  167. arrow.className = CLASS_POPOVER_ARROW;
  168. popover.appendChild(arrow);
  169. }
  170. var arrowSize = arrow && arrow.offsetWidth / 2 || 0;
  171. var pTop = 0;
  172. var pLeft = 0;
  173. var diff = 0;
  174. var arrowLeft = 0;
  175. var defaultPadding = popover.classList.contains(CLASS_ACTION_POPOVER) ? 0 : 5;
  176. var position = 'top';
  177. if ((pHeight + arrowSize) < (offset.top - window.pageYOffset)) { //top
  178. pTop = offset.top - pHeight - arrowSize;
  179. } else if ((pHeight + arrowSize) < (wHeight - (offset.top - window.pageYOffset) - aHeight)) { //bottom
  180. position = 'bottom';
  181. pTop = offset.top + aHeight + arrowSize;
  182. } else { //middle
  183. position = 'middle';
  184. pTop = Math.max((wHeight - pHeight) / 2 + window.pageYOffset, 0);
  185. pLeft = Math.max((wWidth - pWidth) / 2 + window.pageXOffset, 0);
  186. }
  187. if (position === 'top' || position === 'bottom') {
  188. pLeft = aWidth / 2 + offset.left - pWidth / 2;
  189. diff = pLeft;
  190. if (pLeft < defaultPadding) pLeft = defaultPadding;
  191. if (pLeft + pWidth > wWidth) pLeft = wWidth - pWidth - defaultPadding;
  192. if (arrow) {
  193. if (position === 'top') {
  194. arrow.classList.add(CLASS_BOTTOM);
  195. } else {
  196. arrow.classList.remove(CLASS_BOTTOM);
  197. }
  198. diff = diff - pLeft;
  199. arrowLeft = (pWidth / 2 - arrowSize / 2 + diff);
  200. arrowLeft = Math.max(Math.min(arrowLeft, pWidth - arrowSize * 2 - 6), 6);
  201. arrow.setAttribute('style', 'left:' + arrowLeft + 'px');
  202. }
  203. } else if (position === 'middle') {
  204. arrow.setAttribute('style', 'display:none');
  205. }
  206. setStyle(popover, 'block', pTop, pLeft);
  207. };
  208. $.createMask = function(callback) {
  209. var element = document.createElement('div');
  210. element.classList.add(CLASS_BACKDROP);
  211. element.addEventListener($.EVENT_MOVE, $.preventDefault);
  212. element.addEventListener('tap', function() {
  213. mask.close();
  214. });
  215. var mask = [element];
  216. mask._show = false;
  217. mask.show = function() {
  218. mask._show = true;
  219. element.setAttribute('style', 'opacity:1');
  220. document.body.appendChild(element);
  221. return mask;
  222. };
  223. mask._remove = function() {
  224. if (mask._show) {
  225. mask._show = false;
  226. element.setAttribute('style', 'opacity:0');
  227. $.later(function() {
  228. var body = document.body;
  229. element.parentNode === body && body.removeChild(element);
  230. }, 350);
  231. }
  232. return mask;
  233. };
  234. mask.close = function() {
  235. if (callback) {
  236. if (callback() !== false) {
  237. mask._remove();
  238. }
  239. } else {
  240. mask._remove();
  241. }
  242. };
  243. return mask;
  244. };
  245. $.fn.popover = function() {
  246. var args = arguments;
  247. this.each(function() {
  248. $.targets._popover = this;
  249. if (args[0] === 'show' || args[0] === 'hide' || args[0] === 'toggle') {
  250. togglePopover(this, args[1], args[0]);
  251. }
  252. });
  253. };
  254. })(mui, window, document, 'popover');