Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5012dcb

Browse files
authored
Fix iframe resize handler when re-attached to DOM (chartjs#3527)
When the iframe is attached to the DOM, its content is reloaded (invaliding the resize listener) so make sure to install the handler after the iframe is loaded. Optimize resize events by throttling resize process until the next animation frame. Rewrite the unit test "waitForResize" method, the previous one (timeout) was too weak and most tests was failing on FF.
1 parent f288bc7 commit 5012dcb

File tree

2 files changed

+100
-22
lines changed

2 files changed

+100
-22
lines changed

src/core/core.helpers.js

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -943,9 +943,7 @@ module.exports = function(Chart) {
943943
return color(c);
944944
};
945945
helpers.addResizeListener = function(node, callback) {
946-
// Hide an iframe before the node
947946
var iframe = document.createElement('iframe');
948-
949947
iframe.className = 'chartjs-hidden-iframe';
950948
iframe.style.cssText =
951949
'display:block;'+
@@ -966,15 +964,37 @@ module.exports = function(Chart) {
966964
// https://github.com/chartjs/Chart.js/issues/3090
967965
iframe.tabIndex = -1;
968966

969-
// Insert the iframe so that contentWindow is available
970-
node.insertBefore(iframe, node.firstChild);
971-
972967
// Let's keep track of this added iframe and thus avoid DOM query when removing it.
973-
node._chartjs = {
974-
resizer: iframe
968+
var stub = node._chartjs = {
969+
resizer: iframe,
970+
ticking: false
971+
};
972+
973+
// Throttle the callback notification until the next animation frame.
974+
var notify = function() {
975+
if (!stub.ticking) {
976+
stub.ticking = true;
977+
helpers.requestAnimFrame.call(window, function() {
978+
if (stub.resizer) {
979+
stub.ticking = false;
980+
return callback();
981+
}
982+
});
983+
}
975984
};
976985

977-
this.addEvent(iframe.contentWindow || iframe, 'resize', callback);
986+
// If the iframe is re-attached to the DOM, the resize listener is removed because the
987+
// content is reloaded, so make sure to install the handler after the iframe is loaded.
988+
// https://github.com/chartjs/Chart.js/issues/3521
989+
helpers.addEvent(iframe, 'load', function() {
990+
helpers.addEvent(iframe.contentWindow || iframe, 'resize', notify);
991+
992+
// The iframe size might have changed while loading, which can also
993+
// happen if the size has been changed while detached from the DOM.
994+
notify();
995+
});
996+
997+
node.insertBefore(iframe, node.firstChild);
978998
};
979999
helpers.removeResizeListener = function(node) {
9801000
if (!node || !node._chartjs) {
@@ -984,6 +1004,7 @@ module.exports = function(Chart) {
9841004
var iframe = node._chartjs.resizer;
9851005
if (iframe) {
9861006
iframe.parentNode.removeChild(iframe);
1007+
node._chartjs.resizer = null;
9871008
}
9881009

9891010
delete node._chartjs;

test/core.controller.tests.js

Lines changed: 71 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
describe('Chart.Controller', function() {
22

3-
function processResizeEvent(chart, callback) {
4-
setTimeout(callback, 100);
3+
function waitForResize(chart, callback) {
4+
var resizer = chart.chart.canvas.parentNode._chartjs.resizer;
5+
var content = resizer.contentWindow || resizer;
6+
var state = content.document.readyState || 'complete';
7+
var handler = function() {
8+
Chart.helpers.removeEvent(content, 'load', handler);
9+
Chart.helpers.removeEvent(content, 'resize', handler);
10+
setTimeout(callback, 50);
11+
};
12+
13+
Chart.helpers.addEvent(content, state !== 'complete'? 'load' : 'resize', handler);
514
}
615

716
describe('context acquisition', function() {
@@ -358,14 +367,14 @@ describe('Chart.Controller', function() {
358367

359368
var wrapper = chart.chart.canvas.parentNode;
360369
wrapper.style.width = '455px';
361-
processResizeEvent(chart, function() {
370+
waitForResize(chart, function() {
362371
expect(chart).toBeChartOfSize({
363372
dw: 455, dh: 350,
364373
rw: 455, rh: 350,
365374
});
366375

367376
wrapper.style.width = '150px';
368-
processResizeEvent(chart, function() {
377+
waitForResize(chart, function() {
369378
expect(chart).toBeChartOfSize({
370379
dw: 150, dh: 350,
371380
rw: 150, rh: 350,
@@ -398,14 +407,14 @@ describe('Chart.Controller', function() {
398407

399408
var wrapper = chart.chart.canvas.parentNode;
400409
wrapper.style.height = '455px';
401-
processResizeEvent(chart, function() {
410+
waitForResize(chart, function() {
402411
expect(chart).toBeChartOfSize({
403412
dw: 300, dh: 455,
404413
rw: 300, rh: 455,
405414
});
406415

407416
wrapper.style.height = '150px';
408-
processResizeEvent(chart, function() {
417+
waitForResize(chart, function() {
409418
expect(chart).toBeChartOfSize({
410419
dw: 300, dh: 150,
411420
rw: 300, rh: 150,
@@ -440,7 +449,7 @@ describe('Chart.Controller', function() {
440449
var wrapper = chart.chart.canvas.parentNode;
441450
wrapper.style.height = '355px';
442451
wrapper.style.width = '455px';
443-
processResizeEvent(chart, function() {
452+
waitForResize(chart, function() {
444453
expect(chart).toBeChartOfSize({
445454
dw: 455, dh: 355,
446455
rw: 455, rh: 355,
@@ -467,7 +476,7 @@ describe('Chart.Controller', function() {
467476

468477
var canvas = chart.chart.canvas;
469478
canvas.style.display = 'block';
470-
processResizeEvent(chart, function() {
479+
waitForResize(chart, function() {
471480
expect(chart).toBeChartOfSize({
472481
dw: 320, dh: 350,
473482
rw: 320, rh: 350,
@@ -494,7 +503,7 @@ describe('Chart.Controller', function() {
494503

495504
var wrapper = chart.chart.canvas.parentNode;
496505
wrapper.style.display = 'block';
497-
processResizeEvent(chart, function() {
506+
waitForResize(chart, function() {
498507
expect(chart).toBeChartOfSize({
499508
dw: 460, dh: 380,
500509
rw: 460, rh: 380,
@@ -503,6 +512,54 @@ describe('Chart.Controller', function() {
503512
done();
504513
});
505514
});
515+
516+
// https://github.com/chartjs/Chart.js/issues/3521
517+
it('should resize the canvas after the wrapper has been re-attached to the DOM', function(done) {
518+
var chart = acquireChart({
519+
options: {
520+
responsive: true,
521+
maintainAspectRatio: false
522+
}
523+
}, {
524+
canvas: {
525+
style: ''
526+
},
527+
wrapper: {
528+
style: 'width: 320px; height: 350px'
529+
}
530+
});
531+
532+
expect(chart).toBeChartOfSize({
533+
dw: 320, dh: 350,
534+
rw: 320, rh: 350,
535+
});
536+
537+
var wrapper = chart.chart.canvas.parentNode;
538+
var parent = wrapper.parentNode;
539+
parent.removeChild(wrapper);
540+
parent.appendChild(wrapper);
541+
wrapper.style.height = '355px';
542+
543+
waitForResize(chart, function() {
544+
expect(chart).toBeChartOfSize({
545+
dw: 320, dh: 355,
546+
rw: 320, rh: 355,
547+
});
548+
549+
parent.removeChild(wrapper);
550+
wrapper.style.width = '455px';
551+
parent.appendChild(wrapper);
552+
553+
waitForResize(chart, function() {
554+
expect(chart).toBeChartOfSize({
555+
dw: 455, dh: 355,
556+
rw: 455, rh: 355,
557+
});
558+
559+
done();
560+
});
561+
});
562+
});
506563
});
507564

508565
describe('config.options.responsive: true (maintainAspectRatio: true)', function() {
@@ -550,14 +607,14 @@ describe('Chart.Controller', function() {
550607

551608
var wrapper = chart.chart.canvas.parentNode;
552609
wrapper.style.width = '450px';
553-
processResizeEvent(chart, function() {
610+
waitForResize(chart, function() {
554611
expect(chart).toBeChartOfSize({
555612
dw: 450, dh: 225,
556613
rw: 450, rh: 225,
557614
});
558615

559616
wrapper.style.width = '150px';
560-
processResizeEvent(chart, function() {
617+
waitForResize(chart, function() {
561618
expect(chart).toBeChartOfSize({
562619
dw: 150, dh: 75,
563620
rw: 150, rh: 75,
@@ -590,14 +647,14 @@ describe('Chart.Controller', function() {
590647

591648
var wrapper = chart.chart.canvas.parentNode;
592649
wrapper.style.height = '455px';
593-
processResizeEvent(chart, function() {
650+
waitForResize(chart, function() {
594651
expect(chart).toBeChartOfSize({
595652
dw: 320, dh: 160,
596653
rw: 320, rh: 160,
597654
});
598655

599656
wrapper.style.height = '150px';
600-
processResizeEvent(chart, function() {
657+
waitForResize(chart, function() {
601658
expect(chart).toBeChartOfSize({
602659
dw: 320, dh: 160,
603660
rw: 320, rh: 160,
@@ -629,7 +686,7 @@ describe('Chart.Controller', function() {
629686
var canvas = chart.chart.canvas;
630687
var wrapper = canvas.parentNode;
631688
wrapper.style.width = '475px';
632-
processResizeEvent(chart, function() {
689+
waitForResize(chart, function() {
633690
expect(chart).toBeChartOfSize({
634691
dw: 475, dh: 450,
635692
rw: 475, rh: 450,

0 commit comments

Comments
 (0)