From 7a5143aa88ee5c8eae18c9f2d69bf3c2cc91676d Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Tue, 7 Apr 2020 17:08:56 +0200 Subject: [PATCH] Fix: Do not execute ResizeObserver's callbacks when the resized element is invisible. --- src/dom/resizeobserver.js | 6 ++++ tests/dom/resizeobserver.js | 61 +++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/src/dom/resizeobserver.js b/src/dom/resizeobserver.js index 31491ab..d3f0af2 100644 --- a/src/dom/resizeobserver.js +++ b/src/dom/resizeobserver.js @@ -168,6 +168,12 @@ export default class ResizeObserver { ResizeObserver._observerInstance = new ObserverConstructor( entries => { for ( const entry of entries ) { + // Do not execute callbacks for elements that are invisible. + // https://github.com/ckeditor/ckeditor5/issues/6570 + if ( !entry.target.offsetParent ) { + continue; + } + const callbacks = ResizeObserver._getElementCallbacks( entry.target ); if ( callbacks ) { diff --git a/tests/dom/resizeobserver.js b/tests/dom/resizeobserver.js index 795605e..6f65c85 100644 --- a/tests/dom/resizeobserver.js +++ b/tests/dom/resizeobserver.js @@ -27,12 +27,18 @@ describe( 'ResizeObserver()', () => { elementA.id = 'A'; elementB = document.createElement( 'div' ); elementB.id = 'B'; + + document.body.appendChild( elementA ); + document.body.appendChild( elementB ); } ); afterEach( () => { // Make it look like the module was loaded from scratch. ResizeObserver._observerInstance = null; ResizeObserver._elementCallbacks = null; + + elementA.remove(); + elementB.remove(); } ); describe( 'constructor()', () => { @@ -108,6 +114,61 @@ describe( 'ResizeObserver()', () => { observerA.destroy(); } ); + it( 'should not react to resizing of an element if element is invisible', () => { + const callbackA = sinon.spy(); + let resizeCallback; + + testUtils.sinon.stub( global.window, 'ResizeObserver' ).callsFake( callback => { + resizeCallback = callback; + + return { + observe() {}, + unobserve() {} + }; + } ); + + const observerA = new ResizeObserver( elementA, callbackA ); + + elementA.style.display = 'none'; + + resizeCallback( [ + { target: elementA } + ] ); + + sinon.assert.notCalled( callbackA ); + + observerA.destroy(); + } ); + + it( 'should not react to resizing of an element if element\'s parent is invisible', () => { + const callbackA = sinon.spy(); + let resizeCallback; + + testUtils.sinon.stub( global.window, 'ResizeObserver' ).callsFake( callback => { + resizeCallback = callback; + + return { + observe() {}, + unobserve() {} + }; + } ); + + const observerA = new ResizeObserver( elementA, callbackA ); + const parent = document.createElement( 'div' ); + document.body.appendChild( parent ); + parent.appendChild( elementA ); + parent.style.display = 'none'; + + resizeCallback( [ + { target: elementA } + ] ); + + sinon.assert.notCalled( callbackA ); + + parent.remove(); + observerA.destroy(); + } ); + it( 'should be able to observe the same element along with other observers', () => { const callbackA = sinon.spy(); const callbackB = sinon.spy();