diff --git a/src/platform/platform.dom.js b/src/platform/platform.dom.js index 03844deea81..7e7ae1e33c9 100644 --- a/src/platform/platform.dom.js +++ b/src/platform/platform.dom.js @@ -114,15 +114,24 @@ function fromNativeEvent(event, chart) { }; } +function nodeListContains(nodeList, canvas) { + for (const node of nodeList) { + if (node === canvas || node.contains(canvas)) { + return true; + } + } +} + function createAttachObserver(chart, type, listener) { const canvas = chart.canvas; const observer = new MutationObserver(entries => { + let trigger = false; for (const entry of entries) { - for (const node of entry.addedNodes) { - if (node === canvas || node.contains(canvas)) { - return listener(); - } - } + trigger = trigger || nodeListContains(entry.addedNodes, canvas); + trigger = trigger && !nodeListContains(entry.removedNodes, canvas); + } + if (trigger) { + listener(); } }); observer.observe(document, {childList: true, subtree: true}); @@ -132,12 +141,13 @@ function createAttachObserver(chart, type, listener) { function createDetachObserver(chart, type, listener) { const canvas = chart.canvas; const observer = new MutationObserver(entries => { + let trigger = false; for (const entry of entries) { - for (const node of entry.removedNodes) { - if (node === canvas || node.contains(canvas)) { - return listener(); - } - } + trigger = trigger || nodeListContains(entry.removedNodes, canvas); + trigger = trigger && !nodeListContains(entry.addedNodes, canvas); + } + if (trigger) { + listener(); } }); observer.observe(document, {childList: true, subtree: true}); diff --git a/test/specs/core.controller.tests.js b/test/specs/core.controller.tests.js index de338b580a0..12d0df3016a 100644 --- a/test/specs/core.controller.tests.js +++ b/test/specs/core.controller.tests.js @@ -1078,6 +1078,108 @@ describe('Chart', function() { }, 0); }); + // https://github.com/chartjs/Chart.js/issues/9875 + it('should detect detach/attach in series', function(done) { + var chart = acquireChart({ + options: { + responsive: true, + maintainAspectRatio: false + } + }, { + canvas: { + style: '' + }, + wrapper: { + style: 'width: 320px; height: 350px' + } + }); + + var wrapper = chart.canvas.parentNode; + var parent = wrapper.parentNode; + + waitForResize(chart, function() { + expect(chart).toBeChartOfSize({ + dw: 320, dh: 350, + rw: 320, rh: 350, + }); + + done(); + }); + + parent.removeChild(wrapper); + parent.appendChild(wrapper); + }); + + it('should detect detach/attach/detach in series', function(done) { + var chart = acquireChart({ + options: { + responsive: true, + maintainAspectRatio: false + } + }, { + canvas: { + style: '' + }, + wrapper: { + style: 'width: 320px; height: 350px' + } + }); + + var wrapper = chart.canvas.parentNode; + var parent = wrapper.parentNode; + + waitForResize(chart, function() { + fail(); + }); + + parent.removeChild(wrapper); + parent.appendChild(wrapper); + parent.removeChild(wrapper); + + setTimeout(function() { + expect(chart.attached).toBeFalse(); + done(); + }, 100); + }); + + it('should detect attach/detach in series', function(done) { + var chart = acquireChart({ + options: { + responsive: true, + maintainAspectRatio: false + } + }, { + canvas: { + style: '' + }, + wrapper: { + style: 'width: 320px; height: 350px' + } + }); + + var wrapper = chart.canvas.parentNode; + var parent = wrapper.parentNode; + + parent.removeChild(wrapper); + + setTimeout(function() { + expect(chart.attached).toBeFalse(); + + waitForResize(chart, function() { + fail(); + }); + + parent.appendChild(wrapper); + parent.removeChild(wrapper); + + setTimeout(function() { + expect(chart.attached).toBeFalse(); + + done(); + }, 100); + }, 100); + }); + // https://github.com/chartjs/Chart.js/issues/4737 it('should resize the canvas when re-creating the chart', function(done) { var chart = acquireChart({