[FIX] web: make scroll utils properly work with website iframe action

The `scrollTo` method and related utils (jQuery animate, ...) were not
properly adapted to work in the context of multiple documents. E.g. the
website editor code (outside the website iframe) is calling the
`scrollTo` util to scroll to added snippet elements (inside the website
iframe). In that case, the util was performing some searches in the DOM
outside of the website iframe while it was meant to be done inside. The
result was that the scroll was not considering the website fixed navbar
and scrolled "too far".

Another bug is visible only when the scrollbar is left to be the natural
browser one (not on the `#wrapwrap`). In that case, the `scrollTo` util
simply had no effect anymore as the `animate` override of jQuery was not
adapted.

Note: a further commit will move back the scrollbar behavior out of the
`#wrapwrap`, this is thus especially needed.

X-original-commit: ab417de98068afaaa12995726c7c6445f075ca4f
Part-of: odoo/odoo#102785
This commit is contained in:
qsm-odoo
2022-10-09 18:52:48 +02:00
parent e54ade4ca5
commit 09ef0e4663
3 changed files with 14 additions and 10 deletions
+7 -5
View File
@@ -519,11 +519,12 @@ var dom = {
* Computes the size by which a scrolling point should be decreased so that
* the top fixed elements of the page appear above that scrolling point.
*
* @return {Document} [document=window.document]
* @returns {number}
*/
scrollFixedOffset() {
scrollFixedOffset(document = window.document) {
let size = 0;
for (const el of $('.o_top_fixed_element')) {
for (const el of document.querySelectorAll('.o_top_fixed_element')) {
size += $(el).outerHeight();
}
return size;
@@ -552,9 +553,10 @@ var dom = {
// If $scrollable and $el are not in the same document, we can safely
// assume $el is in an $iframe. We retrieve it by filtering the list of
// iframes in $scrollable to keep only the one that contains $el.
const isInOneDocument = $scrollable[0].ownerDocument === $el[0].ownerDocument;
const scrollDocument = $scrollable[0].ownerDocument;
const isInOneDocument = scrollDocument === $el[0].ownerDocument;
const $iframe = !isInOneDocument && $scrollable.find('iframe').filter((i, node) => $(node).contents().has($el));
const $topLevelScrollable = $().getScrollingElement();
const $topLevelScrollable = $().getScrollingElement(scrollDocument);
const isTopScroll = $scrollable.is($topLevelScrollable);
function _computeScrollTop() {
@@ -579,7 +581,7 @@ var dom = {
}
let offset = options.forcedOffset;
if (offset === undefined) {
offset = (isTopScroll ? dom.scrollFixedOffset() : 0) + (options.extraOffset || 0);
offset = (isTopScroll ? dom.scrollFixedOffset(scrollDocument) : 0) + (options.extraOffset || 0);
}
return Math.max(0, elPosition - offset);
}
+5 -3
View File
@@ -201,13 +201,14 @@ $.fn.scrollTop = function (value) {
// The caller wants to scroll a set of elements including html and/or
// body to a specific point -> do that but make sure to add the real
// top level element to that set of elements if any different is found.
originalScrollTop.apply(this.not('html, body').add($().getScrollingElement()), arguments);
const $withRealScrollable = this.not('html, body').add($().getScrollingElement(this[0].ownerDocument));
originalScrollTop.apply($withRealScrollable, arguments);
return this;
} else if (value === undefined && this.eq(0).is('html, body')) {
// The caller wants to get the scroll point of a set of elements, jQuery
// will return the scroll point of the first one, if it is html or body
// return the scroll point of the real top level element.
return originalScrollTop.apply($().getScrollingElement(), arguments);
return originalScrollTop.apply($().getScrollingElement(this[0].ownerDocument), arguments);
}
return originalScrollTop.apply(this, arguments);
};
@@ -218,7 +219,8 @@ $.fn.animate = function (properties, ...rest) {
// The caller wants to scroll a set of elements including html and/or
// body to a specific point -> do that but make sure to add the real
// top level element to that set of elements if any different is found.
originalAnimate.call(this.not('html, body').add($().getScrollingElement()), {'scrollTop': props['scrollTop']}, ...rest);
const $withRealScrollable = this.not('html, body').add($().getScrollingElement(this[0].ownerDocument));
originalAnimate.call($withRealScrollable, {'scrollTop': props['scrollTop']}, ...rest);
delete props['scrollTop'];
}
if (!Object.keys(props).length) {
@@ -1851,8 +1851,8 @@ var SnippetsMenu = Widget.extend({
if (!this.$scrollingElement[0]) {
this.$scrollingElement = $(this.ownerDocument).find('.o_editable');
}
this.$scrollingTarget = this.$scrollingElement.is(this.ownerDocument.scrollingElement)
? $(this.ownerDocument.defaultView)
this.$scrollingTarget = this.$scrollingElement.is(this.$body[0].ownerDocument.scrollingElement)
? $(this.$body[0].ownerDocument.defaultView)
: this.$scrollingElement;
this._onScrollingElementScroll = _.throttle(() => {
for (const editor of this.snippetEditors) {