Aligning a pair of MDC drawers

I am facing a challenge on my webpage where I have implemented two Material Design Component drawers with identical items. One is set to be permanent for desktop and tablet displays, while the other is designed to be hidden or modal for mobile devices.

<aside class="mdc-drawer mdc-drawer--permanent">
    <div class="mdc-drawer__header">
        <h3 class="mdc-drawer__title">App</h3>
        <h6 class="mdc-drawer__subtitle">@username</h6>
    </div>
    <div class="mdc-drawer__content">
        <nav class="mdc-list--permanent">@menu_drawer_content</nav>
    </div>
</aside>

<aside class="mdc-drawer mdc-drawer--modal">
    <div class="mdc-drawer__header">
        <h3 class="mdc-drawer__title">App</h3>
        <h6 class="mdc-drawer__subtitle">@username</h6>
    </div>
    <div class="mdc-drawer__content">
        <nav class="mdc-list">@menu_drawer_content</nav>
    </div>
</aside>

Both drawers are initialized as follows:

modalDrawer = mdc.drawer.MDCDrawer.attachTo(document.querySelector('.mdc-drawer--modal'));
let list = mdc.list.MDCList.attachTo(document.querySelector('.mdc-list--permanent'));
list.wrapFocus = true;

To toggle between the two drawers based on screen size, I use the following JavaScript code:

let smallForm = window.matchMedia("(max-width: 767px)").matches;

function resized() {
    let smallForm_ = window.matchMedia("(max-width: 767px)").matches;
    if (smallForm !== smallForm_) {
        smallForm = smallForm_;
        changedMedia();
    }
}

function changedMedia() {
    let drawerButton = $('.mdc-top-app-bar__row > section > button');
    if (smallForm) {
        $('.mdc-drawer--permanent').hide();
        drawerButton.show();
    } else {
        $('.mdc-drawer--permanent').show();
        drawerButton.hide();
        modalDrawer.open = false;
    }
}

However, there is a bug that persists when selecting an item in one drawer does not sync with the same item in the other drawer. It leads to inconsistencies especially when transitioning between screen sizes.

Is there a way to link these two drawers so that selection in one will affect the state of the other without causing any unwanted recursive loop?

Edit: Added bounty. Full source.

Answer №1

I have discovered a way to "undo" the instantiation of the MDC component, allowing you to use a single drawer and switch between modal and permanent modes while still maintaining the selection of the drawer list item. The key part of the code snippet below is calling destroy() when switching media so that you can successfully modify the drawer class and re-instantiate it.

let timeout;
let activeBar;
let activeDrawer;
let activeList;
const actualResizeHandler = () => {
  const fixedStyles = () => {
    document.body.style = 'display: flex; height: 100vh;';
    document.querySelector('.mdc-drawer-app-content').style = 'flex: auto; overflow: auto;';
    document.querySelector('.main-content').style = 'height: 100%; overflow: auto;';
    document.querySelector('.mdc-top-app-bar').style = 'position: absolute;';
  };

  const modalStyles = () => {
    document.body.removeAttribute('style');
    document.querySelector('.mdc-drawer-app-content').removeAttribute('style');
    document.querySelector('.main-content').removeAttribute('style');
    document.querySelector('.mdc-top-app-bar').removeAttribute('style');
  };
  
  const bar = document.querySelector('.mdc-top-app-bar');
  const drawer = document.querySelector('.mdc-drawer');
  const list = document.querySelector('.mdc-list');
  if (typeof activeBar !== 'undefined') {
    activeBar.destroy();
  }
    
  if (window.matchMedia('(max-width: 767px)').matches) {
    if (typeof activeList !== 'undefined') {
      activeList.destroy();
    }
    
    drawer.classList.add('mdc-drawer--modal');
    drawer.insertAdjacentHTML('afterend', '<div class="mdc-drawer-scrim"></div>');
    modalStyles();
    activeBar = mdc.topAppBar.MDCTopAppBar.attachTo(bar);
    activeBar.listen('MDCTopAppBar:nav', () => {
      if (typeof activeDrawer !== 'undefined') {
        activeDrawer.open = !activeDrawer.open;
      }
      
    });
    
    activeDrawer = mdc.drawer.MDCDrawer.attachTo(drawer);
  } else {
    const scrim = document.querySelector('.mdc-drawer-scrim');
    if (scrim) {
      scrim.remove();
    }
    
    if (typeof activeDrawer !== 'undefined') {
      activeDrawer.destroy();
    }
    
    drawer.classList.remove('mdc-drawer--modal');
    fixedStyles();
    activeList = mdc.list.MDCList.attachTo(list);
    activeList.wrapFocus = true;
  }
};

const resizeThrottler = () => {
  if (!timeout) {
    timeout = setTimeout(() => {
      timeout = null;
      actualResizeHandler();
     }, 66);
  }
};
  
window.addEventListener('resize', resizeThrottler, false);
actualResizeHandler();
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Material Modal / Dismissible Drawer Example</title>
    <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700">
    <link href="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.css" rel="stylesheet">
    <script src="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.js"></script>
  </head>
  <body>
    <aside class="mdc-drawer">
      <div class="mdc-drawer__content">
        <nav class="mdc-list"&
          ...
          
        </nav>
      </div>
    </aside>
    <div class="mdc-drawer-app-content">
      <header class="mdc-top-app-bar">
        <div class="mdc-top-app-bar__row">
          <section class="mdc-top-app-bar__...
           ...icon</button>
            <span class="mdc-top-app-bar__title">Title</span>
          </section>
        </div>
      </header>
      <main class="main-content">
        <div class="mdc-top-app-bar--fixed-adjust">
          App Content
        </div>
      </main>
    </div>
  </body>
</html>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

An error message indicating that the page is currently being unloaded has appeared

While working on a NodeJS-ReactJS Isomorphic App, I encountered an issue when clicking on a Link. An error message popped up saying: Uncaught (in promise) Error: Request has been terminated Possible causes: the network is offline, Origin is not allowed by ...

Incorporating lazy loading for diverse content to enhance pagination

I'm currently using jpaginate for pagination on a large dataset, but I've noticed that all the content loads at once. Is there a jQuery plugin available that will only load the content for the current page? ...

Validating an item within an enumeration

In my enum, I store various UI element values organized as follows: const uiElementAttributes = { 1: { id: 1, color: 'red', icon: 'icon-something.png' }, 2: { id: 2, color: 'yellow', ...

Helping React and MUI components become mobile responsive - Seeking guidance to make it happen

My React component uses Material-UI (MUI) and I'm working on making it mobile responsive. Here's how it looks currently: https://i.sstatic.net/kxsSD.png But this is the look I want to achieve: https://i.sstatic.net/kJC2m.png Below is the code ...

Tips for effectively organizing a collapsible list

Here is a list that I have: <ul> <li><span class="Collapsable">item 1</span> <ul> <li><span class="Collapsable">item 1.1</span></li> </ul> </ul> I am looking to create ...

Creating image galleries with HTML and Javascript loop through the drawImage function to display multiple

I'm currently working on developing a card game using HTML5 canvas, and I've come across an issue with the drawImage function when used inside a loop. It seems like there might be a problem related to closures, but I'm not entirely sure how ...

Using AngularJS $http.jsonp() method to interface with Google Maps Distance Matrix API

I am currently working on integrating the Google Maps Distance Matrix API into my project to calculate distances between two points using specific coordinates. My implementation involves AngularJS and the $http.jsonp() method to make requests to the API: ...

Menu design with child element creatively integrated into parent element

For my mobile project, I am working on creating a window blind effect menu. The process is quite simple: Clicking on the menu icon moves the bar to the top while the menu drops down Upon selecting an item, the menu smoothly "rolls" up in a window blind s ...

Is it possible to access the ID of a collection_select and have a list of items appear whenever the selection is modified?

I am working on a form named _form.html.erb <%= simple_form_for(@exam) do |f| %> <%= f.error_notification %> <div class="field"> <%= f.label :Year %> <%= f.collection_select :year_id, Year.order(:name), :id, :name, ...

What are the best practices for utilizing the "this" keyword with fetch?

After extensively studying ES6 documentation and the changes it brings, I am eager to incorporate the new oop syntax along with modern libraries such as fetch. Below is the code snippet I have been working on: apiCall(url, thisAlias=this){ fetch(url). ...

The material-ui library always registers Event.ctrlKey as true

When using the Table Component from the material-ui library, I encountered an issue with the multiSelectable feature. Setting the value of multiSelectable to true allows for multiple selections, but the behavior is not what I expected. By default, the sel ...

Turn off Satellizer Popup Window Title Bar

Currently, I am implementing the satellizer plugin for Facebook authentication. However, I have encountered an issue with the default popup login window of Facebook, which includes a title bar and menu options. I would like to transform this popup into a m ...

Interactive Icon Feature Instead of Annoying Pop-Ups in JavaScript

Hello there! I need assistance fixing a code issue. Currently, the code automatically pops up when the page is opened. Is there a way to make it clickable instead of popping up automatically? <script type="text/javascript" src="//www.klaviyo.com/media/ ...

How can the Node app utilize an HTML page to reference another JavaScript file? Ran into an unexpected syntax error: `Uncaught SyntaxError: Unexpected token '<

I'm trying to figure out how to call another script from an HTML page that is being served by my node project's localhost server. Here's the basic setup: index.js var http = require('http'); var fileSystem = require('fs' ...

After a page reload, Material-UI stops functioning properly

I am currently working with Material UI in a Next.js project. When I run npm run dev, everything looks good. However, whenever I refresh the page, all the styling breaks. Has anyone experienced this issue before? It seems like Material-UI is no longer func ...

What is the significance of static in react?

export class NavMenu extends Component { static displayName = NavMenu.name; constructor (props) { super(props); this.toggleNavbar = this.toggleNavbar.bind(this); this.state = { collapsed: true }; } I attempted to research the ...

What is the most effective method for incorporating personalized React components in the midst of strings or paragraph tags

Summary: Exploring the frontend world for the first time. Attempting to integrate custom components within p-tags for a website, but facing challenges in making them dynamically changeable based on user interaction. Greetings all! As a newbie in front-end ...

Converting JQuery Object (Ajax-response) to an Array Containing Keys and Values

When I make an $.ajax request, the response I receive looks like this: https://i.sstatic.net/UfnfQ.jpg (please don't worry about the Russian symbols, they are just strings of text:)) I am aware that when PHP sends the request, it is in a simple arr ...

Tips for concealing information that is scrolled beneath a translucent layer

In the scenario where you have two overlapping divs, with the top one being transparent, the goal is to have the bottom div hide as it goes under the top transparent div when scrolling. It's important that the bottom div's visibility is not compl ...

After an Ajax form is completed within the .done function, a separate Ajax call must be

Within the confines of an ajax done function lies a form (It's important to note that it resides inside the .done function) $.ajax({ type: "POST", url: "?select_main", data: {}, }) .done(function(data) { <div>\n\ <form id= ...