diff --git a/draftlogs/8017_change.md b/draftlogs/8017_change.md new file mode 100644 index 00000000000..6be3df02dd3 --- /dev/null +++ b/draftlogs/8017_change.md @@ -0,0 +1 @@ +Use `ResizeObserver` for responsive chart resizing instead of listening for window resize events [[#8017](https://github.com/plotly/plotly.js/pull/8017)], with thanks to @Lexachoc for the contribution! diff --git a/src/lib/clear_responsive.js b/src/lib/clear_responsive.js index a2e33f26a1c..643e6cade88 100644 --- a/src/lib/clear_responsive.js +++ b/src/lib/clear_responsive.js @@ -6,8 +6,8 @@ * @param {DOM node or object} gd : graph div object */ module.exports = function clearResponsive(gd) { - if(gd._responsiveChartHandler) { - window.removeEventListener('resize', gd._responsiveChartHandler); - delete gd._responsiveChartHandler; + if (gd._clearResponsive) { + gd._clearResponsive(); + delete gd._clearResponsive; } }; diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index 0adcd88b12b..f48d808425b 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -165,16 +165,34 @@ function _doPlot(gd, data, layout, config) { gd.calcdata[i][0].trace = gd._fullData[i]; } - // make the figure responsive + // Make the figure responsive. We need to save the callback that clears the + // listener for proper teardown. if (gd._context.responsive) { - if (!gd._responsiveChartHandler) { - // Keep a reference to the resize handler to purge it down the road - gd._responsiveChartHandler = function () { + if (!gd._clearResponsive) { + const resizeIfShown = () => { if (!Lib.isHidden(gd)) Plots.resize(gd); }; - - // Listen to window resize - window.addEventListener('resize', gd._responsiveChartHandler); + // We still need the window resize listener for `fillFrame` and an + // escape hatch for browser-like users that don't support `ResizeObserver` (like jsdom) + if (gd._context.fillFrame || typeof ResizeObserver === 'undefined') { + window.addEventListener('resize', resizeIfShown); + gd._clearResponsive = () => window.removeEventListener('resize', resizeIfShown); + } else { + let previousWidth = gd.offsetWidth; + let previousHeight = gd.offsetHeight; + const observer = new ResizeObserver(() => { + const width = gd.offsetWidth; + const height = gd.offsetHeight; + // Ignore size changes of one pixel or less (the same as plotAutoSize) + const changed = Math.abs(width - previousWidth) > 1 || Math.abs(height - previousHeight) > 1; + previousWidth = width; + previousHeight = height; + // Only resize plot if it changed and is visible (width and height > 0) + if (changed && width && height) resizeIfShown(); + }); + observer.observe(gd); + gd._clearResponsive = () => observer.disconnect(); + } } } else { Lib.clearResponsive(gd); diff --git a/src/plot_api/plot_config.js b/src/plot_api/plot_config.js index abbb689b673..dc67084bb35 100644 --- a/src/plot_api/plot_config.js +++ b/src/plot_api/plot_config.js @@ -128,8 +128,8 @@ var configAttributes = { valType: 'boolean', dflt: false, description: [ - 'Determines whether to change the layout size when window is resized.', - 'In v3, this option will be removed and will always be true.' + 'Determines whether to change the layout size when the graph container is resized.', + 'In v5, this option will be removed and will always be true.' ].join(' ') }, fillFrame: { diff --git a/src/types/generated/schema.d.ts b/src/types/generated/schema.d.ts index 4766d954d4c..c0a9eab9fad 100644 --- a/src/types/generated/schema.d.ts +++ b/src/types/generated/schema.d.ts @@ -16809,7 +16809,7 @@ export interface ConfigBase { */ queueLength?: number; /** - * Determines whether to change the layout size when window is resized. In v3, this option will be removed and will always be true. + * Determines whether to change the layout size when the graph container is resized. In v5, this option will be removed and will always be true. * @default false */ responsive?: boolean; diff --git a/test/plot-schema.json b/test/plot-schema.json index 39767518c96..c821c1d5ec4 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -279,7 +279,7 @@ "valType": "integer" }, "responsive": { - "description": "Determines whether to change the layout size when window is resized. In v3, this option will be removed and will always be true.", + "description": "Determines whether to change the layout size when the graph container is resized. In v5, this option will be removed and will always be true.", "dflt": false, "valType": "boolean" },