Strange behavior on mobile chrome and modals

433 Views Asked by At

I recently got informed that mobile users on chrome/android are experiencing a little "funny" behavior on modals. The autofocus does not work as expected and the view is always scrolled to the bottom.

This happens on modals, which are embedded at the end of the page.

Examples

Sandbox

I created this little sandbox (https://codesandbox.io/s/little-firefly-ruytgy?file=/src/App.vue) to demonstrate the issue.

Screencast

I also created a little screencast.

enter image description here

Code example

This is the code of App.vue in this example:

<template>
  <div class="h-screen">
    <div class="h-96 bg-red-500 flex justify-center items-center">
      <span class="text-white">Scroll down...</span>
    </div>
    <div class="h-96 bg-red-500 flex justify-center items-center">
      <span class="text-white">Just a little bit more...</span>
    </div>
    <div class="h-96 bg-red-500 flex justify-center items-center">
      <span class="text-white">You are almost there...</span>
    </div>
    <div class="h-96 bg-red-500 flex justify-center items-center">
      <span class="text-white"
        >Here we are. Now scroll down and click the button...</span
      >
    </div>

    <SimpleModal ref="mymodal">
      <template #dialog>
        <InputField name="test1" v-model="value1" />
        <InputField name="test2" v-model="value2" />
        <InputField name="test3" v-model="value3" />
        <InputField name="test4" v-model="value4" />
        <InputField name="test5" v-model="value5" />
        <InputField name="test6" v-model="value6" />
        <InputField name="test7" v-model="value7" />
        <InputField name="test7" v-model="value7" />
        <InputField name="test7" v-model="value7" />
        <InputField name="test7" v-model="value7" />
        <InputField name="test7" v-model="value7" />
        <InputField name="test7" v-model="value7" />
        <InputField name="test7" v-model="value7" />
        <InputField name="test7" v-model="value7" />
        <InputField name="test7" v-model="value7" />
        <InputField name="test7" v-model="value7" />
        <InputField name="test7" v-model="value7" />
        <InputField name="test7" v-model="value7" />
        <InputField name="test7" v-model="value7" />
        <InputField name="test7" v-model="value7" />
        <InputField name="test7" v-model="value7" />
        <InputField name="test7" v-model="value7" />
      </template>
    </SimpleModal>
    <div class="flex justify-center items-center mt-4">
      <button class="border p-4" @click="$refs.mymodal.openModal">
        Open Modal
      </button>
    </div>
  </div>
</template>
<script>
import { ref } from "vue";
import InputField from "./components/InputField.vue";
import SimpleModal from "./components/SimpleModal.vue";

export default {
  components: {
    SimpleModal,
    InputField,
  },

  setup() {
    const isOpen = ref(false);

    return {
      isOpen,
      closeModal() {
        isOpen.value = false;
      },
      openModal() {
        isOpen.value = true;
      },
    };
  },
  data() {
    return {
      value1: null,
      value2: null,
      value3: null,
      value4: null,
    };
  },
};
</script>

Ugly workaround

For now I created a little ugly workaround: Before the modal is being opened, use

window.scroll(0,0);

to scroll to the top. That's not really what I want, because if the person closes the modal, the person has to scroll down all the way again.

2

There are 2 best solutions below

0
On

First, I'm not great with CSS, so it's very possible that there's a more elegant solution that what I'm suggesting.

I notice that the modal/dialog does not have a max-height and is ending up using the available height on the page. This is what is leading to the long modal in the first place---both on desktop and mobile. One way to fix the issue you're running into is to simply give it a max-height.

src/components/SimpleModal.vue
Add a class (e.g. .dialog) to the modal container (parent of DialogTitle) and simply apply some CSS rules to it.

<style scoped>
// Tailwind seems to be applying the following rules:
// margin-top: 2rem;
// margin-bottom: 2rem;
// Take control of this if needed and apply it ourselves

.dialog {
  --dialog-margin-y: 2rem;
  --dialog-max-height: 100vh;
  margin-top: var(--dialog-margin-y);
  margin-bottom: var(--dialog-margin-y);
  max-height: calc(var(--dialog-max-height) - calc(2 * var(--dialog-margin-y)));
  overflow-y: auto; // We're going to need this since we're fixing max-height
}
</style>

With these rules in place, this is what I observe:

Desktop enter image description here

Mobile enter image description here

Here's a screengrab of my Android device

enter image description here

0
On

Your modal is getting generated while you scrolled to the bottom and there is no place below for modal to take place. so the modal will get upper place and your viewpoint will be the bottom of it.

So now as a workaround I would give the modal a fixed height or at least a max height based on view height.

Give this max-height: calc(100vh - 64px) and this overflow-y: auto to your modal container.

Now your modal will take up to 100vh - 64px of the screen height and if it has more content it will scroll inside the modal.

It will prevent other UI bugs in the future. for example if your modal height be more that page height it will cause a UI bug. with this solution you will prevent it.

.modal-container {
  max-height: calc(100vh - 64px);
  overflow-y: auto;
}