From 589db692828f637b0c0a5adb02fb6b6f777a401d Mon Sep 17 00:00:00 2001 From: Jake Lauritsen <59895043+jakelauritsen@users.noreply.github.com> Date: Tue, 23 Jul 2024 13:40:00 -0700 Subject: [PATCH] Fix screen resizer memory leak for mdl layout (#6) * Fix mem leak * Make screenResizeHanlder local and use change event * Make the media query global * Loop over all layouts * Update src/layout/layout.js Co-authored-by: Ryan Brown * Only add one listener for all layouts * Formatting adjustments * Clean Linter * Add nvmrc * Clean linter * Fix es5 error * Fixing tests that rely on window.mediaQuery * Clean linter * No need for .nvmrc * Register a mock media list if one exists on the static def of layout * Make screenSizeMediaQuery static and add update docs * Use static method for storing the matchMedia * Make matchMedia static and update doc for closure * Fix test typo --------- Co-authored-by: Ryan Brown --- src/layout/layout.js | 63 ++++++++++++++++++++++++++------------------ test/unit/layout.js | 11 +++++--- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/src/layout/layout.js b/src/layout/layout.js index a82c67307..1dfcb1056 100644 --- a/src/layout/layout.js +++ b/src/layout/layout.js @@ -16,7 +16,6 @@ */ (function() { 'use strict'; - /** * Class constructor for Layout MDL component. * Implements MDL component design pattern defined at: @@ -136,10 +135,14 @@ * * @private */ - MaterialLayout.prototype.matchMedia_ = function(query) { + MaterialLayout.matchMedia_ = function(query) { return window.matchMedia(query); }; + MaterialLayout.screenSizeMediaQuery_ = MaterialLayout.matchMedia_( + /** @type {string} */ (MaterialLayout.prototype.Constant_.MAX_WIDTH)); + MaterialLayout.screenSizeMediaQuery_.onchange = screenSizeHandler; + /** * Handles scrolling on the content. * @@ -186,31 +189,44 @@ }; /** - * Handles changes in screen size. + * Handles screen size changes by updating the layout and drawer elements + * based on the media query change event status. + * + * @param {!MediaQueryList|!MediaQueryListEvent} m - is any object that provides matches * - * @private */ - MaterialLayout.prototype.screenSizeHandler_ = function() { - if (this.screenSizeMediaQuery_.matches) { - this.element_.classList.add(this.CssClasses_.IS_SMALL_SCREEN); + function screenSizeHandler(m) { + // modified to query dependent elements rather than binding materialLayout to windows media query result + var materialLayouts = document.querySelectorAll('.mdl-layout'); - if (this.drawer_) { - this.drawer_.setAttribute('aria-hidden', 'true'); - } - } else { - this.element_.classList.remove(this.CssClasses_.IS_SMALL_SCREEN); - // Collapse drawer (if any) when moving to a large screen size. - if (this.drawer_) { - this.drawer_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN); - this.obfuscator_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN); + for (var i = 0; i < materialLayouts.length; i++) { + var layout = materialLayouts[i]; - if (this.element_.classList.contains(this.CssClasses_.FIXED_DRAWER)) { - this.drawer_.setAttribute('aria-hidden', 'false'); + if (layout) { + var drawerElement = layout.querySelector('.mdl-layout__drawer'); + + if (m.matches) { + layout.classList.add('is-small-screen'); + if (drawerElement) { + drawerElement.setAttribute('aria-hidden', 'true'); + } + } else { + layout.classList.remove('is-small-screen'); + // Collapse drawer (if any) when moving to a large screen size. + if (drawerElement) { + drawerElement.classList.remove('is-visible'); + var obfuscator = layout.querySelector('.mdl-layout__obfuscator'); // corrected selector + if (obfuscator) { + obfuscator.classList.remove('is-visible'); + } + if (layout.classList.contains('mdl-layout--fixed-drawer')) { + drawerElement.setAttribute('aria-hidden', 'false'); + } + } } } } - }; - + } /** * Handles events of drawer button. * @@ -223,7 +239,6 @@ // prevent scrolling in drawer nav evt.preventDefault(); } else { - // prevent other keys return; } } @@ -433,10 +448,8 @@ // Keep an eye on screen size, and add/remove auxiliary class for styling // of small screens. - this.screenSizeMediaQuery_ = this.matchMedia_( - /** @type {string} */ (this.Constant_.MAX_WIDTH)); - this.screenSizeMediaQuery_.addListener(this.screenSizeHandler_.bind(this)); - this.screenSizeHandler_(); + + screenSizeHandler(MaterialLayout.screenSizeMediaQuery_); // Initialize tabs, if any. if (this.header_ && this.tabBar_) { diff --git a/test/unit/layout.js b/test/unit/layout.js index 688314f45..219ec0eef 100644 --- a/test/unit/layout.js +++ b/test/unit/layout.js @@ -22,6 +22,10 @@ describe('MaterialLayout', function () { } MockMediaQueryList.registry = {}; + if(MaterialLayout.screenSizeMediaQuery_){ + var query = window.MaterialLayout.prototype.Constant_.MAX_WIDTH + MockMediaQueryList.registry[query] = new MockMediaQueryList(query) + } MockMediaQueryList.mockMatchMedia = function(query) { if (! MockMediaQueryList.registry.hasOwnProperty(query)) { @@ -131,9 +135,8 @@ describe('MaterialLayout', function () { var navLink; beforeEach(function() { - this.originalMatchMedia = window.MaterialLayout.prototype.matchMedia_; - window.MaterialLayout.prototype.matchMedia_ = MockMediaQueryList.mockMatchMedia; - window.patched = 'yes patched'; + this.originalMatchMedia = MaterialLayout.matchMedia_; + MaterialLayout.matchMedia_ = MockMediaQueryList.mockMatchMedia; el = document.createElement('div'); el.innerHTML = '
' + @@ -157,7 +160,7 @@ describe('MaterialLayout', function () { }); afterEach(function() { - window.MaterialLayout.prototype.matchMedia_ = this.originalMatchMedia; + MaterialLayout.matchMedia_ = this.originalMatchMedia; }); it('should have attribute aria-hidden="true"', function () {