From ecbaddd5e1af7d776275a815799525530e98dd9b Mon Sep 17 00:00:00 2001 From: chenjiahan Date: Sat, 12 Sep 2020 22:04:02 +0800 Subject: [PATCH] feat(use): add useScrollParent --- packages/vant-use/src/index.ts | 1 + .../vant-use/src/useScrollParent/index.ts | 51 +++++++++++++++++++ src/composition/use-scroller.ts | 14 ----- src/dropdown-menu/index.js | 7 ++- src/index-bar/index.js | 30 ++++++----- src/list/index.js | 27 +++++----- src/pull-refresh/index.js | 6 +-- src/sticky/index.js | 7 ++- 8 files changed, 91 insertions(+), 52 deletions(-) create mode 100644 packages/vant-use/src/useScrollParent/index.ts delete mode 100644 src/composition/use-scroller.ts diff --git a/packages/vant-use/src/index.ts b/packages/vant-use/src/index.ts index f562c40c5..dd687cca0 100644 --- a/packages/vant-use/src/index.ts +++ b/packages/vant-use/src/index.ts @@ -1,3 +1,4 @@ export { useToggle } from './useToggle'; export { useClickAway } from './useClickAway'; +export { useScrollParent } from './useScrollParent'; export { useEventListener } from './useEventListener'; diff --git a/packages/vant-use/src/useScrollParent/index.ts b/packages/vant-use/src/useScrollParent/index.ts new file mode 100644 index 000000000..8b1731d9e --- /dev/null +++ b/packages/vant-use/src/useScrollParent/index.ts @@ -0,0 +1,51 @@ +import { ref, Ref, onMounted } from 'vue'; + +type ScrollElement = HTMLElement | Window; + +const overflowScrollReg = /scroll|auto/i; + +function isElement(node: Element) { + const ELEMENT_NODE_TYPE = 1; + return node.tagName !== 'HTML' && node.nodeType === ELEMENT_NODE_TYPE; +} + +// http://w3help.org/zh-cn/causes/SD9013 +// http://stackoverflow.com/questions/17016740/onscroll-function-is-not-working-for-chrome +export function getScrollParent(el: Element, root: ScrollElement = window) { + let node = el; + + while (node && node !== root && isElement(node)) { + const { overflowY } = window.getComputedStyle(node); + + if (overflowScrollReg.test(overflowY)) { + if (node.tagName !== 'BODY') { + return node; + } + + // see: https://github.com/youzan/vant/issues/3823 + const { overflowY: htmlOverflowY } = window.getComputedStyle( + node.parentNode as Element + ); + + if (overflowScrollReg.test(htmlOverflowY)) { + return node; + } + } + + node = node.parentNode as Element; + } + + return root; +} + +export function useScrollParent(el: Ref) { + const scrollParent = ref(); + + onMounted(() => { + if (el.value) { + scrollParent.value = getScrollParent(el.value); + } + }); + + return scrollParent; +} diff --git a/src/composition/use-scroller.ts b/src/composition/use-scroller.ts deleted file mode 100644 index efbafe358..000000000 --- a/src/composition/use-scroller.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Ref, ref, onMounted } from 'vue'; -import { getScroller } from '../utils/dom/scroll'; - -export function useScroller(el: Ref) { - const scrollerRef = ref(); - - onMounted(() => { - if (el.value) { - scrollerRef.value = getScroller(el.value); - } - }); - - return scrollerRef; -} diff --git a/src/dropdown-menu/index.js b/src/dropdown-menu/index.js index ad060d9b0..858dec090 100644 --- a/src/dropdown-menu/index.js +++ b/src/dropdown-menu/index.js @@ -4,9 +4,8 @@ import { ref, provide, reactive, computed } from 'vue'; import { createNamespace, isDef } from '../utils'; // Composition -import { useClickAway, useEventListener } from '@vant/use'; +import { useClickAway, useScrollParent, useEventListener } from '@vant/use'; import { useRect } from '../composition/use-rect'; -import { useScroller } from '../composition/use-scroller'; const [createComponent, bem] = createNamespace('dropdown-menu'); @@ -40,7 +39,7 @@ export default createComponent({ const rootRef = ref(); const children = reactive([]); - const scroller = useScroller(rootRef); + const scrollParent = useScrollParent(rootRef); const opened = computed(() => children.some((item) => item.state.showWrapper) @@ -123,7 +122,7 @@ export default createComponent({ useClickAway(rootRef, onClickAway); - useEventListener('scroll', onScroll, { target: scroller }); + useEventListener('scroll', onScroll, { target: scrollParent }); return () => (
diff --git a/src/index-bar/index.js b/src/index-bar/index.js index 8583bbdbc..226ae6f77 100644 --- a/src/index-bar/index.js +++ b/src/index-bar/index.js @@ -12,10 +12,9 @@ import { } from '../utils/dom/scroll'; // Composition -import { useEventListener } from '@vant/use'; +import { useScrollParent, useEventListener } from '@vant/use'; import { useRect } from '../composition/use-rect'; import { useTouch } from '../composition/use-touch'; -import { useScroller } from '../composition/use-scroller'; export const INDEX_BAR_KEY = 'vanIndexBar'; @@ -58,7 +57,7 @@ export default createComponent({ const children = reactive([]); const touch = useTouch(); - const scroller = useScroller(rootRef); + const scrollParent = useScrollParent(rootRef); provide(INDEX_BAR_KEY, { props, children }); @@ -79,8 +78,8 @@ export default createComponent({ }); const getScrollerRect = () => { - if (scroller.value.getBoundingClientRect) { - return useRect(scroller); + if (scrollParent.value.getBoundingClientRect) { + return useRect(scrollParent); } return { top: 0, @@ -88,13 +87,16 @@ export default createComponent({ }; }; - const getAnchorTop = (element, scrollerRect) => { - if (scroller.value === window || scroller.value === document.body) { + const getAnchorTop = (element, scrollParentRect) => { + if ( + scrollParent.value === window || + scrollParent.value === document.body + ) { return getElementTop(element); } const rect = useRect(element); - return rect.top - scrollerRect.top + getScrollTop(scroller); + return rect.top - scrollParentRect.top + getScrollTop(scrollParent); }; const getActiveAnchor = (scrollTop, rects) => { @@ -116,12 +118,12 @@ export default createComponent({ } const { sticky, indexList } = props; - const scrollTop = getScrollTop(scroller.value); - const scrollerRect = getScrollerRect(); + const scrollTop = getScrollTop(scrollParent.value); + const scrollParentRect = getScrollerRect(); const rects = children.map((item) => ({ height: item.height, - top: getAnchorTop(item.rootRef, scrollerRect), + top: getAnchorTop(item.rootRef, scrollParentRect), })); const active = getActiveAnchor(scrollTop, rects); @@ -144,11 +146,11 @@ export default createComponent({ state.active = true; state.top = Math.max(props.stickyOffsetTop, rects[index].top - scrollTop) + - scrollerRect.top; + scrollParentRect.top; } else if (index === active - 1) { const activeItemTop = rects[active].top - scrollTop; state.active = activeItemTop > 0; - state.top = activeItemTop + scrollerRect.top - height; + state.top = activeItemTop + scrollParentRect.top - height; } else { state.active = false; } @@ -156,7 +158,7 @@ export default createComponent({ } }; - useEventListener('scroll', onScroll, { target: scroller }); + useEventListener('scroll', onScroll, { target: scrollParent }); watch( () => props.indexList, diff --git a/src/list/index.js b/src/list/index.js index c3ae9ac20..2639be501 100644 --- a/src/list/index.js +++ b/src/list/index.js @@ -5,9 +5,8 @@ import { createNamespace } from '../utils'; import { isHidden } from '../utils/dom/style'; // Composition -import { useEventListener } from '@vant/use'; +import { useScrollParent, useEventListener } from '@vant/use'; import { useRect } from '../composition/use-rect'; -import { useScroller } from '../composition/use-scroller'; import { usePublicApi } from '../composition/use-public-api'; // Components @@ -44,7 +43,7 @@ export default createComponent({ const loading = ref(false); const rootRef = ref(); const placeholderRef = ref(); - const scroller = useScroller(rootRef); + const scrollParent = useScrollParent(rootRef); const check = () => { nextTick(() => { @@ -53,21 +52,22 @@ export default createComponent({ } const { offset, direction } = props; - let scrollerRect; + let scrollParentRect; - if (scroller.value.getBoundingClientRect) { - scrollerRect = scroller.value.getBoundingClientRect(); + if (scrollParent.value.getBoundingClientRect) { + scrollParentRect = scrollParent.value.getBoundingClientRect(); } else { - scrollerRect = { + scrollParentRect = { top: 0, - bottom: scroller.value.innerHeight, + bottom: scrollParent.value.innerHeight, }; } - const scrollerHeight = scrollerRect.bottom - scrollerRect.top; + const scrollParentHeight = + scrollParentRect.bottom - scrollParentRect.top; /* istanbul ignore next */ - if (!scrollerHeight || isHidden(rootRef.value)) { + if (!scrollParentHeight || isHidden(rootRef.value)) { return false; } @@ -75,9 +75,10 @@ export default createComponent({ const placeholderRect = useRect(placeholderRef); if (direction === 'up') { - isReachEdge = scrollerRect.top - placeholderRect.top <= offset; + isReachEdge = scrollParentRect.top - placeholderRect.top <= offset; } else { - isReachEdge = placeholderRect.bottom - scrollerRect.bottom <= offset; + isReachEdge = + placeholderRect.bottom - scrollParentRect.bottom <= offset; } if (isReachEdge) { @@ -143,7 +144,7 @@ export default createComponent({ usePublicApi({ check }); - useEventListener('scroll', check, { target: scroller }); + useEventListener('scroll', check, { target: scrollParent }); return () => { const Content = slots.default?.(); diff --git a/src/pull-refresh/index.js b/src/pull-refresh/index.js index acf092897..83a907d62 100644 --- a/src/pull-refresh/index.js +++ b/src/pull-refresh/index.js @@ -6,8 +6,8 @@ import { getScrollTop } from '../utils/dom/scroll'; import { preventDefault } from '../utils/dom/event'; // Composition +import { useScrollParent } from '@vant/use'; import { useTouch } from '../composition/use-touch'; -import { useScroller } from '../composition/use-scroller'; // Components import Loading from '../loading'; @@ -48,7 +48,7 @@ export default createComponent({ let reachTop; const rootRef = ref(); - const scroller = useScroller(rootRef); + const scrollParent = useScrollParent(rootRef); const state = reactive({ status: 'normal', @@ -130,7 +130,7 @@ export default createComponent({ }; const checkPosition = (event) => { - reachTop = getScrollTop(scroller.value) === 0; + reachTop = getScrollTop(scrollParent.value) === 0; if (reachTop) { state.duration = 0; diff --git a/src/sticky/index.js b/src/sticky/index.js index b2f149562..a9f05cf88 100644 --- a/src/sticky/index.js +++ b/src/sticky/index.js @@ -7,8 +7,7 @@ import { createNamespace } from '../utils'; import { getScrollTop, getElementTop } from '../utils/dom/scroll'; // Composition -import { useEventListener } from '@vant/use'; -import { useScroller } from '../composition/use-scroller'; +import { useScrollParent, useEventListener } from '@vant/use'; import { useVisibilityChange } from '../composition/use-visibility-change'; const [createComponent, bem] = createNamespace('sticky'); @@ -27,7 +26,7 @@ export default createComponent({ setup(props, { emit, slots }) { const rootRef = ref(); - const scroller = useScroller(rootRef); + const scrollParent = useScrollParent(rootRef); const state = reactive({ fixed: false, @@ -101,7 +100,7 @@ export default createComponent({ emitScrollEvent(scrollTop); }; - useEventListener('scroll', onScroll, { target: scroller }); + useEventListener('scroll', onScroll, { target: scrollParent }); useVisibilityChange(rootRef, onScroll);