Private Simple Tooltips

Private Simple

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/534448/1580338/Private%20Simple%20Tooltips.js

  1. function enableTooltips() {
  2. const createTooltip = ({
  3. target,
  4. message,
  5. position = 'top',
  6. bgColor = '#333',
  7. color = '#fff',
  8. motion = 'fade',
  9. delay = 0,
  10. duration = 2000,
  11. onHide = null,
  12. }) => {
  13. if (target._tooltipActive) return;
  14. target._tooltipActive = true;
  15.  
  16. const tooltip = document.createElement('div');
  17. const arrow = document.createElement('div');
  18. tooltip.appendChild(arrow);
  19. document.body.appendChild(tooltip);
  20.  
  21. // 스타일 초기화
  22. Object.assign(tooltip.style, {
  23. position: 'absolute',
  24. padding: '6px 10px',
  25. borderRadius: '6px',
  26. fontSize: '14px',
  27. pointerEvents: 'none',
  28. whiteSpace: 'nowrap',
  29. zIndex: '9999',
  30. opacity: '0',
  31. transition: 'opacity 0.3s ease, transform 0.3s ease',
  32. backgroundColor: bgColor,
  33. color,
  34. maxWidth: 'calc(100vw - 20px)',
  35. });
  36.  
  37. Object.assign(arrow.style, {
  38. position: 'absolute',
  39. width: '0',
  40. height: '0',
  41. });
  42.  
  43. const setArrowStyle = () => {
  44. arrow.style.border = 'none';
  45. switch (position) {
  46. case 'top':
  47. Object.assign(arrow.style, {
  48. bottom: '-6px',
  49. left: '50%',
  50. transform: 'translateX(-50%)',
  51. borderLeft: '6px solid transparent',
  52. borderRight: '6px solid transparent',
  53. borderTop: '6px solid #333',
  54. });
  55. break;
  56. case 'bottom':
  57. Object.assign(arrow.style, {
  58. top: '-6px',
  59. left: '50%',
  60. transform: 'translateX(-50%)',
  61. borderLeft: '6px solid transparent',
  62. borderRight: '6px solid transparent',
  63. borderBottom: '6px solid #333',
  64. });
  65. break;
  66. case 'left':
  67. Object.assign(arrow.style, {
  68. top: '50%',
  69. right: '-6px',
  70. transform: 'translateY(-50%)',
  71. borderTop: '6px solid transparent',
  72. borderBottom: '6px solid transparent',
  73. borderLeft: '6px solid #333',
  74. });
  75. break;
  76. case 'right':
  77. Object.assign(arrow.style, {
  78. top: '50%',
  79. left: '-6px',
  80. transform: 'translateY(-50%)',
  81. borderTop: '6px solid transparent',
  82. borderBottom: '6px solid transparent',
  83. borderRight: '6px solid #333',
  84. });
  85. break;
  86. }
  87. };
  88.  
  89. const show = () => {
  90. const rect = target.getBoundingClientRect();
  91. const scrollY = window.scrollY || 0;
  92. const scrollX = window.scrollX || 0;
  93.  
  94. let x = rect.left + rect.width / 2 + scrollX;
  95. let y = rect.top + scrollY;
  96.  
  97. tooltip.innerText = message;
  98. tooltip.appendChild(arrow);
  99. setArrowStyle();
  100.  
  101. switch (position) {
  102. case 'top':
  103. y = rect.top - 0 + scrollY;
  104. tooltip.style.transform = motion === 'slide' ? 'translate(-50%, -120%)' : 'translate(-50%, -100%)';
  105. break;
  106. case 'bottom':
  107. y = rect.bottom + 0 + scrollY;
  108. tooltip.style.transform = motion === 'slide' ? 'translate(-50%, 20px)' : 'translate(-50%, 10px)';
  109. break;
  110. case 'left':
  111. x = rect.left - 0 + scrollX;
  112. y = rect.top + rect.height / 2 + scrollY;
  113. tooltip.style.transform = motion === 'slide' ? 'translate(-120%, -50%)' : 'translate(-105%, -50%)';
  114. break;
  115. case 'right':
  116. x = rect.right + 0 + scrollX;
  117. y = rect.top + rect.height / 2 + scrollY;
  118. tooltip.style.transform = motion === 'slide' ? 'translate(20%, -50%)' : 'translate(5%, -50%)';
  119. break;
  120. }
  121.  
  122. tooltip.style.left = `${x}px`;
  123. tooltip.style.top = `${y}px`;
  124.  
  125. requestAnimationFrame(() => {
  126. tooltip.style.opacity = '1';
  127. });
  128.  
  129. // 위치 보정 (툴팁이 화면 밖으로 벗어나지 않게)
  130. requestAnimationFrame(() => {
  131. const tipRect = tooltip.getBoundingClientRect();
  132. const padding = 8;
  133. const overflowRight = tipRect.right - window.innerWidth;
  134. const overflowLeft = -tipRect.left;
  135.  
  136. if (overflowRight > 0) {
  137. tooltip.style.left = `${x - overflowRight - padding}px`;
  138. }
  139. if (overflowLeft > 0) {
  140. tooltip.style.left = `${x + overflowLeft + padding}px`;
  141. }
  142. });
  143.  
  144. // 사라짐 처리
  145. setTimeout(() => {
  146. tooltip.style.opacity = '0';
  147. setTimeout(() => {
  148. tooltip.remove();
  149. target._tooltipActive = false;
  150. if (typeof window[onHide] === 'function') {
  151. window[onHide](target);
  152. }
  153. }, 300);
  154. }, duration);
  155. };
  156.  
  157. setTimeout(show, delay);
  158. };
  159.  
  160. // 바인딩
  161. const elements = document.querySelectorAll('[tooltip]');
  162. elements.forEach(el => {
  163. const handler = () => {
  164. const content = el.getAttribute('tooltip');
  165. if (!content) return;
  166.  
  167. createTooltip({
  168. target: el,
  169. message: content,
  170. position: el.getAttribute('tooltip-position') || 'top',
  171. bgColor: el.getAttribute('tooltip-bg') || '#333',
  172. color: el.getAttribute('tooltip-color') || '#fff',
  173. motion: el.getAttribute('tooltip-motion') || 'fade',
  174. delay: parseInt(el.getAttribute('tooltip-delay') || '0', 10),
  175. duration: parseInt(el.getAttribute('tooltip-duration') || '2000', 10),
  176. onHide: el.getAttribute('tooltip-on-hide'),
  177. });
  178. };
  179.  
  180. const useHover = el.getAttribute('tooltip-hover') === 'true';
  181.  
  182. if (useHover) {
  183. el.addEventListener('mouseenter', handler);
  184. el.addEventListener('mouseleave', () => {
  185. // 자동 제거는 내부적으로 처리됨
  186. });
  187. } else {
  188. el.addEventListener('click', handler);
  189. el.addEventListener('touchstart', handler);
  190. el.addEventListener('focusin', handler);
  191. }
  192. });
  193. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址