position.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. angular.module('ui.bootstrap.position', [])
  2. /**
  3. * A set of utility methods that can be use to retrieve position of DOM elements.
  4. * It is meant to be used where we need to absolute-position DOM elements in
  5. * relation to other, existing elements (this is the case for tooltips, popovers,
  6. * typeahead suggestions etc.).
  7. */
  8. .factory('$position', ['$document', '$window', function($document, $window) {
  9. function getStyle(el, cssprop) {
  10. if (el.currentStyle) { //IE
  11. return el.currentStyle[cssprop];
  12. } else if ($window.getComputedStyle) {
  13. return $window.getComputedStyle(el)[cssprop];
  14. }
  15. // finally try and get inline style
  16. return el.style[cssprop];
  17. }
  18. /**
  19. * Checks if a given element is statically positioned
  20. * @param element - raw DOM element
  21. */
  22. function isStaticPositioned(element) {
  23. return (getStyle(element, 'position') || 'static' ) === 'static';
  24. }
  25. /**
  26. * returns the closest, non-statically positioned parentOffset of a given element
  27. * @param element
  28. */
  29. var parentOffsetEl = function(element) {
  30. var docDomEl = $document[0];
  31. var offsetParent = element.offsetParent || docDomEl;
  32. while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {
  33. offsetParent = offsetParent.offsetParent;
  34. }
  35. return offsetParent || docDomEl;
  36. };
  37. return {
  38. /**
  39. * Provides read-only equivalent of jQuery's position function:
  40. * http://api.jquery.com/position/
  41. */
  42. position: function(element) {
  43. var elBCR = this.offset(element);
  44. var offsetParentBCR = { top: 0, left: 0 };
  45. var offsetParentEl = parentOffsetEl(element[0]);
  46. if (offsetParentEl != $document[0]) {
  47. offsetParentBCR = this.offset(angular.element(offsetParentEl));
  48. offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
  49. offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
  50. }
  51. var boundingClientRect = element[0].getBoundingClientRect();
  52. return {
  53. width: boundingClientRect.width || element.prop('offsetWidth'),
  54. height: boundingClientRect.height || element.prop('offsetHeight'),
  55. top: elBCR.top - offsetParentBCR.top,
  56. left: elBCR.left - offsetParentBCR.left
  57. };
  58. },
  59. /**
  60. * Provides read-only equivalent of jQuery's offset function:
  61. * http://api.jquery.com/offset/
  62. */
  63. offset: function(element) {
  64. var boundingClientRect = element[0].getBoundingClientRect();
  65. return {
  66. width: boundingClientRect.width || element.prop('offsetWidth'),
  67. height: boundingClientRect.height || element.prop('offsetHeight'),
  68. top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop),
  69. left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)
  70. };
  71. },
  72. /**
  73. * Provides coordinates for the targetEl in relation to hostEl
  74. */
  75. positionElements: function(hostEl, targetEl, positionStr, appendToBody) {
  76. var positionStrParts = positionStr.split('-');
  77. var pos0 = positionStrParts[0], pos1 = positionStrParts[1] || 'center';
  78. var hostElPos,
  79. targetElWidth,
  80. targetElHeight,
  81. targetElPos;
  82. hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl);
  83. targetElWidth = targetEl.prop('offsetWidth');
  84. targetElHeight = targetEl.prop('offsetHeight');
  85. var shiftWidth = {
  86. center: function() {
  87. return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
  88. },
  89. left: function() {
  90. return hostElPos.left;
  91. },
  92. right: function() {
  93. return hostElPos.left + hostElPos.width;
  94. }
  95. };
  96. var shiftHeight = {
  97. center: function() {
  98. return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
  99. },
  100. top: function() {
  101. return hostElPos.top;
  102. },
  103. bottom: function() {
  104. return hostElPos.top + hostElPos.height;
  105. }
  106. };
  107. switch (pos0) {
  108. case 'right':
  109. targetElPos = {
  110. top: shiftHeight[pos1](),
  111. left: shiftWidth[pos0]()
  112. };
  113. break;
  114. case 'left':
  115. targetElPos = {
  116. top: shiftHeight[pos1](),
  117. left: hostElPos.left - targetElWidth
  118. };
  119. break;
  120. case 'bottom':
  121. targetElPos = {
  122. top: shiftHeight[pos0](),
  123. left: shiftWidth[pos1]()
  124. };
  125. break;
  126. default:
  127. targetElPos = {
  128. top: hostElPos.top - targetElHeight,
  129. left: shiftWidth[pos1]()
  130. };
  131. break;
  132. }
  133. return targetElPos;
  134. }
  135. };
  136. }]);