From 63020feb8e26d9d227d445a31bd8e27adf995d52 Mon Sep 17 00:00:00 2001 From: mertsincan Date: Sun, 27 Sep 2020 18:27:08 +0300 Subject: [PATCH] Fixed #500 - Improvement in behavior of components that have overlay panels in scrollable containers --- .../utils/ConnectedOverlayScrollHandler.js | 39 ++++++++++++++++ src/components/utils/DomHandler.js | 46 +++++++++++++++++-- 2 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 src/components/utils/ConnectedOverlayScrollHandler.js diff --git a/src/components/utils/ConnectedOverlayScrollHandler.js b/src/components/utils/ConnectedOverlayScrollHandler.js new file mode 100644 index 000000000..794cc83bc --- /dev/null +++ b/src/components/utils/ConnectedOverlayScrollHandler.js @@ -0,0 +1,39 @@ +import DomHandler from './DomHandler'; +import UniqueComponentId from './UniqueComponentId'; + +export default class ConnectedOverlayScrollHandler { + + constructor(element, elementId = UniqueComponentId(), listener = () => {}) { + this.element = element; + this.elementId = elementId; + this.listener = listener; + } + + bindScrollListener() { + this.scrollableParents = DomHandler.getScrollableParents(this.element); + this.scrollListeners = {}; + for (let i = 0; i < this.scrollableParents.length; i++) { + let parent = this.scrollableParents[i]; + let namespace = `${this.elementId}_${i}`; + + if (!this.scrollListeners[namespace]) { + this.scrollListeners[namespace] = this.listener; + parent.addEventListener('scroll', this.scrollListeners[namespace]); + } + } + } + + unbindScrollListener() { + if (this.scrollableParents) { + for (let i = 0; i < this.scrollableParents.length; i++) { + let parent = this.scrollableParents[i]; + let namespace = `${this.elementId}_${i}`; + + if (this.scrollListeners[namespace]) { + parent.removeEventListener('scroll', this.scrollListeners[namespace]); + this.scrollListeners[namespace] = null; + } + } + } + } +} diff --git a/src/components/utils/DomHandler.js b/src/components/utils/DomHandler.js index 4dc91dfd8..e7eeaebe8 100755 --- a/src/components/utils/DomHandler.js +++ b/src/components/utils/DomHandler.js @@ -245,6 +245,42 @@ export default class DomHandler { element.style.left = left + 'px'; } + static getParents(element, parents = []) { + return element['parentNode'] === null ? parents : this.getParents(element.parentNode, parents.concat([element.parentNode])); + } + + static getScrollableParents(element) { + let scrollableParents = []; + + if (element) { + let parents = this.getParents(element); + const overflowRegex = /(auto|scroll)/; + const overflowCheck = (node) => { + let styleDeclaration = window['getComputedStyle'](node, null); + return overflowRegex.test(styleDeclaration.getPropertyValue('overflow')) || overflowRegex.test(styleDeclaration.getPropertyValue('overflowX')) || overflowRegex.test(styleDeclaration.getPropertyValue('overflowY')); + }; + + for (let parent of parents) { + let scrollSelectors = parent.nodeType === 1 && parent.dataset['scrollselectors']; + if (scrollSelectors) { + let selectors = scrollSelectors.split(','); + for (let selector of selectors) { + let el = this.findSingle(parent, selector); + if (el && overflowCheck(el)) { + scrollableParents.push(el); + } + } + } + + if (parent.nodeType === 9 || overflowCheck(parent)) { + scrollableParents.push(parent); + } + } + } + + return scrollableParents; + } + static getHiddenElementOuterHeight(element) { element.style.visibility = 'hidden'; element.style.display = 'block'; @@ -423,10 +459,10 @@ export default class DomHandler { } static getFocusableElements(element) { - let focusableElements = DomHandler.find(element, `button:not([tabindex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden]), - [href][clientHeight][clientWidth]:not([tabindex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden]), - input:not([tabindex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden]), select:not([tabindex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden]), - textarea:not([tabindex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden]), [tabIndex]:not([tabIndex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden]), + let focusableElements = DomHandler.find(element, `button:not([tabindex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden]), + [href][clientHeight][clientWidth]:not([tabindex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden]), + input:not([tabindex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden]), select:not([tabindex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden]), + textarea:not([tabindex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden]), [tabIndex]:not([tabIndex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden]), [contenteditable]:not([tabIndex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden])` ); @@ -449,4 +485,4 @@ export default class DomHandler { this.hasClass(element.parentElement, 'p-checkbox') || this.hasClass(element.parentElement, 'p-radiobutton') ); } -} \ No newline at end of file +}