|
23 | 23 | const HTML_RE = /^\s*<([a-z][\w-]*)[\s\S]*>/i; |
24 | 24 | // WeakMap<Element, Map<event, Array<{ user, listener, selector }>>> |
25 | 25 | const listenerStore = isClient ? new WeakMap() : null; |
| 26 | + // WeakMap<Element, string> — stores original display value before hide() |
| 27 | + const displayStore = isClient ? new WeakMap() : null; |
26 | 28 |
|
27 | 29 | function getListenerMap(el, event) { |
28 | 30 | let map = listenerStore.get(el); |
|
237 | 239 | * @returns {GStime} The GStime object for chaining. |
238 | 240 | */ |
239 | 241 | GStime.prototype.hide = function () { |
240 | | - return this.css("display", "none"); |
| 242 | + return this.each(function () { |
| 243 | + if (this.nodeType === 1) { |
| 244 | + const current = this.style.display; |
| 245 | + if (current !== "none") { |
| 246 | + displayStore.set(this, current); |
| 247 | + } |
| 248 | + this.style.display = "none"; |
| 249 | + } |
| 250 | + }); |
241 | 251 | }; |
242 | | - |
| 252 | + |
243 | 253 | /** |
244 | 254 | * Displays the matched elements. |
245 | 255 | * @returns {GStime} The GStime object for chaining. |
246 | 256 | */ |
247 | 257 | GStime.prototype.show = function () { |
248 | | - return this.css("display", ""); |
| 258 | + return this.each(function () { |
| 259 | + if (this.nodeType === 1) { |
| 260 | + const prev = displayStore.get(this); |
| 261 | + this.style.display = prev || ""; |
| 262 | + displayStore.delete(this); |
| 263 | + } |
| 264 | + }); |
249 | 265 | }; |
250 | | - |
| 266 | + |
251 | 267 | /** |
252 | 268 | * Toggles the visibility of the matched elements. |
253 | 269 | * @returns {GStime} The GStime object for chaining. |
254 | 270 | */ |
255 | 271 | GStime.prototype.toggle = function () { |
256 | 272 | return this.each(function () { |
257 | | - this.style.display = this.style.display === "none" ? "" : "none"; |
| 273 | + if (getComputedStyle(this).display === "none") { |
| 274 | + new GStime(this).show(); |
| 275 | + } else { |
| 276 | + new GStime(this).hide(); |
| 277 | + } |
258 | 278 | }); |
259 | 279 | }; |
260 | 280 |
|
|
703 | 723 | * Gets the siblings of each element in the set of matched elements. |
704 | 724 | * @returns {GStime} A new GStime object containing the sibling elements. |
705 | 725 | */ |
706 | | - GStime.prototype.siblings = function () { |
707 | | - const siblings = this.elements.flatMap((el) => |
708 | | - Array.from(el.parentNode.children).filter((child) => child !== el) |
709 | | - ); |
710 | | - return new GStime(siblings); |
711 | | - }; |
| 726 | + GStime.prototype.siblings = function () { |
| 727 | + const siblings = this.elements.flatMap((el) => |
| 728 | + el.parentNode |
| 729 | + ? Array.from(el.parentNode.children).filter((child) => child !== el) |
| 730 | + : [] |
| 731 | + ); |
| 732 | + return new GStime(siblings); |
| 733 | + }; |
712 | 734 |
|
713 | 735 | /** |
714 | 736 | * Finds descendants of each element in the current set of matched elements. |
|
849 | 871 | * @param {function} [callback] - A function to call once the animation is complete. |
850 | 872 | * @returns {GStime} The GStime object for chaining. |
851 | 873 | */ |
852 | | - GStime.prototype.slideDown = function (duration, callback) { |
853 | | - return this.each(function () { |
854 | | - const el = this; |
855 | | - const prevDisplay = el.style.display; |
856 | | - |
857 | | - // Make sure the element is visible but with zero height |
858 | | - el.style.overflow = 'hidden'; |
859 | | - if (getComputedStyle(el).display === 'none') el.style.display = 'block'; |
860 | | - el.style.height = '0px'; |
861 | | - el.style.paddingTop = '0px'; |
862 | | - el.style.paddingBottom = '0px'; |
863 | | - el.style.marginTop = '0px'; |
864 | | - el.style.marginBottom = '0px'; |
865 | | - |
866 | | - // Get natural height |
867 | | - const targetHeight = el.scrollHeight; |
868 | | - |
869 | | - // Start animation |
870 | | - requestAnimationFrame(() => { |
871 | | - el.style.transition = `height ${duration}ms, padding ${duration}ms, margin ${duration}ms`; |
872 | | - el.style.height = `${targetHeight}px`; |
873 | | - el.style.paddingTop = ''; |
874 | | - el.style.paddingBottom = ''; |
875 | | - el.style.marginTop = ''; |
876 | | - el.style.marginBottom = ''; |
| 874 | + GStime.prototype.slideDown = function (duration, callback) { |
| 875 | + return this.each(function () { |
| 876 | + const el = this; |
| 877 | + const wasHidden = getComputedStyle(el).display === 'none'; |
877 | 878 |
|
878 | | - let done = false; |
879 | | - const finish = function () { |
880 | | - if (done) return; |
881 | | - done = true; |
882 | | - el.style.height = ''; |
883 | | - el.style.overflow = ''; |
884 | | - el.style.transition = ''; |
885 | | - if (prevDisplay) el.style.display = prevDisplay; |
886 | | - el.removeEventListener('transitionend', onTransitionEnd); |
887 | | - if (typeof callback === 'function') callback.call(el); |
888 | | - }; |
889 | | - const onTransitionEnd = function (e) { |
890 | | - if (e.target !== el || e.propertyName !== 'height') return; |
891 | | - finish(); |
892 | | - }; |
893 | | - el.addEventListener('transitionend', onTransitionEnd); |
894 | | - setTimeout(finish, duration + 50); |
| 879 | + // Store original inline display; if it is "none" we should not restore it |
| 880 | + const prevDisplay = el.style.display && el.style.display !== 'none' |
| 881 | + ? el.style.display |
| 882 | + : ''; |
| 883 | + |
| 884 | + el.style.overflow = 'hidden'; |
| 885 | + if (wasHidden) { |
| 886 | + el.style.display = ''; |
| 887 | + } |
| 888 | + // If still hidden after clearing inline style, force block |
| 889 | + if (getComputedStyle(el).display === 'none') { |
| 890 | + el.style.display = 'block'; |
| 891 | + } |
| 892 | + el.style.height = '0px'; |
| 893 | + el.style.paddingTop = '0px'; |
| 894 | + el.style.paddingBottom = '0px'; |
| 895 | + el.style.marginTop = '0px'; |
| 896 | + el.style.marginBottom = '0px'; |
| 897 | + |
| 898 | + // Get natural height |
| 899 | + const targetHeight = el.scrollHeight; |
| 900 | + |
| 901 | + // Start animation |
| 902 | + requestAnimationFrame(() => { |
| 903 | + el.style.transition = `height ${duration}ms, padding ${duration}ms, margin ${duration}ms`; |
| 904 | + el.style.height = `${targetHeight}px`; |
| 905 | + el.style.paddingTop = ''; |
| 906 | + el.style.paddingBottom = ''; |
| 907 | + el.style.marginTop = ''; |
| 908 | + el.style.marginBottom = ''; |
| 909 | + |
| 910 | + let done = false; |
| 911 | + const finish = function () { |
| 912 | + if (done) return; |
| 913 | + done = true; |
| 914 | + el.style.height = ''; |
| 915 | + el.style.overflow = ''; |
| 916 | + el.style.transition = ''; |
| 917 | + if (wasHidden && prevDisplay) { |
| 918 | + el.style.display = prevDisplay; |
| 919 | + } |
| 920 | + el.removeEventListener('transitionend', onTransitionEnd); |
| 921 | + if (typeof callback === 'function') callback.call(el); |
| 922 | + }; |
| 923 | + const onTransitionEnd = function (e) { |
| 924 | + if (e.target !== el || e.propertyName !== 'height') return; |
| 925 | + finish(); |
| 926 | + }; |
| 927 | + el.addEventListener('transitionend', onTransitionEnd); |
| 928 | + setTimeout(finish, duration + 50); |
| 929 | + }); |
895 | 930 | }); |
896 | | - }); |
897 | | - }; |
| 931 | + }; |
898 | 932 |
|
899 | 933 | /** |
900 | 934 | * Toggles between slideUp and slideDown for the matched elements. |
|
0 commit comments