Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve performance in cases when table is hiding #10490

Merged
merged 5 commits into from Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changelogs/10490.json
@@ -0,0 +1,8 @@
{
"issuesOrigin": "private",
"title": "Improved the performance of the table in cases when the table is hiding",
"type": "changed",
"issueOrPR": 10490,
"breaking": false,
"framework": "none"
}
67 changes: 66 additions & 1 deletion handsontable/src/helpers/dom/__tests__/element.unit.js
Expand Up @@ -8,6 +8,7 @@ import {
removeClass,
selectElementIfAllowed,
fastInnerHTML,
isVisible,
} from 'handsontable/helpers/dom/element';

describe('DomElement helper', () => {
Expand Down Expand Up @@ -444,7 +445,7 @@ describe('DomElement helper', () => {
});

//
// Handsontable.helper.sanitize
// Handsontable.helper.fastInnerHTML
//
describe('fastInnerHTML', () => {
it('should be possible to sanitize the HTML (by default the content is sanitized)', () => {
Expand Down Expand Up @@ -504,4 +505,68 @@ describe('DomElement helper', () => {
.toBe('<meta http-equiv="refresh" content="30">This is my <a href="https://handsontable.com">link</a>');
});
});

//
// Handsontable.helper.isVisible
//
describe('isVisible', () => {
it('should return `false` when the element is detached from the DOM', () => {
const element = document.createElement('div');

expect(isVisible(element)).toBe(false);
});

it('should return `true` when the element is attached to the DOM', () => {
const element = document.createElement('div');

document.body.appendChild(element);

expect(isVisible(element)).toBe(true);

element.remove();
});

it('should return `false` when the element has "display: none"', () => {
const element = document.createElement('div');

element.style.display = 'none';
document.body.appendChild(element);

expect(isVisible(element)).toBe(false);

element.remove();
});

it('should return `true` when the element has other value than "display: none"', () => {
const element = document.createElement('div');

document.body.appendChild(element);
element.style.display = 'static';

expect(isVisible(element)).toBe(true);

element.style.display = 'absolute';

expect(isVisible(element)).toBe(true);

element.style.display = '';

expect(isVisible(element)).toBe(true);

element.remove();
});

it('should return `false` when the parent element has "display: none"', () => {
const elementParent = document.createElement('div');
const elementChild = document.createElement('div');

elementParent.append(elementChild);
elementParent.style.display = 'none';
document.body.appendChild(elementParent);

expect(isVisible(elementParent)).toBe(false);

elementParent.remove();
});
});
});
3 changes: 1 addition & 2 deletions handsontable/src/helpers/dom/element.js
Expand Up @@ -350,15 +350,14 @@ export function isVisible(element) {

} else if (next.host) { // Chrome 33.0.1723.0 canary (2013-11-29) Web Platform features enabled
return isVisible(next.host);

}
throw new Error('Lost in Web Components world');

} else {
return false; // this is a node detached from document in IE8
}

} else if (next.style && next.style.display === 'none') {
} else if (getComputedStyle(next).display === 'none') {
return false;
}

Expand Down
3 changes: 2 additions & 1 deletion handsontable/src/tableView.js
Expand Up @@ -9,6 +9,7 @@ import {
isChildOf,
isInput,
isOutsideInput,
isVisible,
} from './helpers/dom/element';
import EventManager from './eventManager';
import { isImmediatePropagationStopped, isRightClick, isLeftClick } from './helpers/dom/event';
Expand Down Expand Up @@ -720,7 +721,7 @@ class TableView {
}
},
onContainerElementResize: () => {
if (this.instance && !this.instance.isDestroyed) {
if (this.instance && !this.instance.isDestroyed && isVisible(this.instance.rootElement)) {
this.instance.refreshDimensions();
}
},
Expand Down
36 changes: 32 additions & 4 deletions handsontable/test/e2e/hooks/afterRefreshDimensions.spec.js
Expand Up @@ -15,7 +15,6 @@ describe('Hook', () => {
describe('afterRefreshDimensions', () => {
it('should be fired after root element size change', async() => {
const afterRefreshDimensions = jasmine.createSpy('afterRefreshDimensions');

const hot = handsontable({
width: 120,
height: 100,
Expand All @@ -28,9 +27,8 @@ describe('Hook', () => {
expect(afterRefreshDimensions.calls.count()).toBe(1);
});

it('should be fired with proper arguments (when root element is size changed)', async() => {
it('should be fired with proper arguments (when root element size is changed)', async() => {
const afterRefreshDimensions = jasmine.createSpy('afterRefreshDimensions');

const hot = handsontable({
width: 120,
height: 100,
Expand All @@ -49,7 +47,6 @@ describe('Hook', () => {

it('should be fired with proper arguments (when root element size does not changed)', async() => {
const afterRefreshDimensions = jasmine.createSpy('afterRefreshDimensions');

const hot = handsontable({
width: 120,
height: 100,
Expand All @@ -66,6 +63,37 @@ describe('Hook', () => {
);
});

it('should not be fired when the table\'s root element is hidden', async() => {
const afterRefreshDimensions = jasmine.createSpy('afterRefreshDimensions');
const hot = handsontable({
width: 120,
height: 100,
afterRefreshDimensions,
});

hot.rootElement.style.display = 'none';
await sleep(50);

expect(afterRefreshDimensions).not.toHaveBeenCalled();
});

it('should not be fired when the document body element is hidden', async() => {
const afterRefreshDimensions = jasmine.createSpy('afterRefreshDimensions');

handsontable({
width: 120,
height: 100,
afterRefreshDimensions,
});

document.body.style.display = 'none';
await sleep(50);

expect(afterRefreshDimensions).not.toHaveBeenCalled();

document.body.style.display = '';
});

it('should be synced with `requestAnimationFrame` call', async() => {
const afterRefreshDimensions = jasmine.createSpy('afterRefreshDimensions');

Expand Down
31 changes: 31 additions & 0 deletions handsontable/test/e2e/hooks/beforeRefreshDimensions.spec.js
Expand Up @@ -87,6 +87,37 @@ describe('Hook', () => {
);
});

it('should not be fired when the table\'s root element is hidden', async() => {
const beforeRefreshDimensions = jasmine.createSpy('beforeRefreshDimensions');
const hot = handsontable({
width: 120,
height: 100,
beforeRefreshDimensions,
});

hot.rootElement.style.display = 'none';
await sleep(50);

expect(beforeRefreshDimensions).not.toHaveBeenCalled();
});

it('should not be fired when the document body element is hidden', async() => {
const beforeRefreshDimensions = jasmine.createSpy('beforeRefreshDimensions');

handsontable({
width: 120,
height: 100,
beforeRefreshDimensions,
});

document.body.style.display = 'none';
await sleep(50);

expect(beforeRefreshDimensions).not.toHaveBeenCalled();

document.body.style.display = '';
});

it('should be synced with `requestAnimationFrame` call', async() => {
const beforeRefreshDimensions = jasmine.createSpy('beforeRefreshDimensions');

Expand Down