Wrong focus after closing a 2nd modal
Asked Answered
M

3

6

I'm using vue.js 2 and bootsrap 3 to open a modal that opens a 2nd modal.

Few days ago, I asked a question on how to set a focus on a control contained in a 2nd modal. I got a great answer that solved the issue.

Problem When opening the 1st modal, the user is able to scroll through it to see its bottom. But after opening and closing the 2nd modal, the focus moves to the page that contains the 1st modal. and when the user scrolls to see the rest of the 1st modal, he scrolls the page behind that 1st modal.

It is very uncomfortable to use especially when the modal is bigger than the screen height. Is there a way to prevent this?

To reproduce this issue, open the answer and click on "Expand snippet"

Mammiemammiferous answered 8/9, 2017 at 11:49 Comment(0)
B
3

Here is a modified version of the previous answer that sets the focus back to the original modal after the sub modal is closed.

The change is here:

$(this.$refs.submodal.$el).on("hidden.bs.modal", this.onShown)

This is added in the mounted handler. It adds a handler to the hidden.bs.modal event of the sub modal. It also removes the handler when the component is destroyed.

Additionally, because closing a modal removes the modal-open class that is assigned to the body when a modal opens, the code below adds that class to the body whenever onShown is called so that the scroll is not affected for the parent modal.

$("body").addClass("modal-open")

Here is a working example.

console.clear()

Vue.component("sub-modal", {
  template: "#submodal",
  methods: {
    show() {
      $(this.$el).modal("show")
    },
    onShown(event) {
      console.log("submodal onshown")
      this.$refs.input.focus()
    }
  },
  mounted() {
   $(this.$el).on("shown.bs.modal", this.onShown)
  },
  beforeDestroy() {
    $(this.$el).off("shown.bs.modal", this.onShown)
  }
})

Vue.component("modal", {
  template: "#modal",
  methods: {
    show() {
      $(this.$refs.modal).modal("show")
    },
    showSubModal() {
      this.$refs.submodal.show()
    },
    onShown(event) {
      console.log("parent")
      this.$refs.input.focus()
      // Add the "modal-open" class back to the body in case
      // it was removed by the sub modal
      $("body").addClass("modal-open")
    }
  },
  mounted() {
    $(this.$refs.modal).on("shown.bs.modal", this.onShown)
    $(this.$refs.submodal.$el).on("hidden.bs.modal", this.onShown)
  },
  beforeDestroy() {
    $(this.$refs.modal).off("shown.bs.modal", this.onShown)
    $(this.$refs.submodal.$el).on("hidden.bs.modal", this.onShown)
  }
})

new Vue({
  el: "#app",
})
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />


<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<div id="app">
  <modal ref="modal"></modal>
  <button @click="$refs.modal.show()" class="btn">Show Modal</button>
</div>

<template id="submodal">
  <div class="modal fade" tabindex="-1" role="dialog">
    <div class="modal-dialog" role="document">
      <div class="modal-content">
        <div class="modal-header">
          <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
          <h4 class="modal-title">Modal title</h4>
        </div>
        <div class="modal-body">
          <input ref="input" type="text" class="form-control">
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
          <button type="button" class="btn btn-primary">Save changes</button>
        </div>
      </div><!-- /.modal-content -->
    </div><!-- /.modal-dialog -->
  </div><!-- /.modal -->

</template>

<template id="modal">
  <div>
    <div ref="modal" class="modal fade" tabindex="-1" role="dialog">

      <div class="modal-dialog modal-lg" role="document">
        <div class="modal-content">
          <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
            <h4 class="modal-title">Modal title</h4>
          </div>
          <div class="modal-body" style="height: 80vh">
            Stuff
            <input ref="input" type="text" class="form-control">
          </div>
          <div class="modal-footer">
            <button @click="showSubModal" type="button" class="btn btn-primary">Show Sub Modal</button>
          </div>
        </div><!-- /.modal-content -->
      </div><!-- /.modal-dialog -->
    </div><!-- /.modal -->
    <sub-modal ref="submodal"></sub-modal>
  </div>
</template>
Bolshevik answered 8/9, 2017 at 15:9 Comment(3)
Unfortunately the problem is still there.. as you can see here imgur.com/1D2CWwp, the scroll bar for #modal disappears after closing #submodal. you are then unable to scroll up to see the top of #modalMammiemammiferous
@Mammiemammiferous check it now, it should work. In general, I think opening a modal from another modal is a bad idea, but this should get you there.Bolshevik
@Mammiemammiferous There is some text added right before the example regarding modal-open.Bolshevik
W
4

That work for me:

$('#my-modal').on('hidden.bs.modal', function () {
        $('body').addClass('modal-open');
    });
Wien answered 25/7, 2018 at 13:47 Comment(0)
B
3

Here is a modified version of the previous answer that sets the focus back to the original modal after the sub modal is closed.

The change is here:

$(this.$refs.submodal.$el).on("hidden.bs.modal", this.onShown)

This is added in the mounted handler. It adds a handler to the hidden.bs.modal event of the sub modal. It also removes the handler when the component is destroyed.

Additionally, because closing a modal removes the modal-open class that is assigned to the body when a modal opens, the code below adds that class to the body whenever onShown is called so that the scroll is not affected for the parent modal.

$("body").addClass("modal-open")

Here is a working example.

console.clear()

Vue.component("sub-modal", {
  template: "#submodal",
  methods: {
    show() {
      $(this.$el).modal("show")
    },
    onShown(event) {
      console.log("submodal onshown")
      this.$refs.input.focus()
    }
  },
  mounted() {
   $(this.$el).on("shown.bs.modal", this.onShown)
  },
  beforeDestroy() {
    $(this.$el).off("shown.bs.modal", this.onShown)
  }
})

Vue.component("modal", {
  template: "#modal",
  methods: {
    show() {
      $(this.$refs.modal).modal("show")
    },
    showSubModal() {
      this.$refs.submodal.show()
    },
    onShown(event) {
      console.log("parent")
      this.$refs.input.focus()
      // Add the "modal-open" class back to the body in case
      // it was removed by the sub modal
      $("body").addClass("modal-open")
    }
  },
  mounted() {
    $(this.$refs.modal).on("shown.bs.modal", this.onShown)
    $(this.$refs.submodal.$el).on("hidden.bs.modal", this.onShown)
  },
  beforeDestroy() {
    $(this.$refs.modal).off("shown.bs.modal", this.onShown)
    $(this.$refs.submodal.$el).on("hidden.bs.modal", this.onShown)
  }
})

new Vue({
  el: "#app",
})
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />


<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<div id="app">
  <modal ref="modal"></modal>
  <button @click="$refs.modal.show()" class="btn">Show Modal</button>
</div>

<template id="submodal">
  <div class="modal fade" tabindex="-1" role="dialog">
    <div class="modal-dialog" role="document">
      <div class="modal-content">
        <div class="modal-header">
          <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
          <h4 class="modal-title">Modal title</h4>
        </div>
        <div class="modal-body">
          <input ref="input" type="text" class="form-control">
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
          <button type="button" class="btn btn-primary">Save changes</button>
        </div>
      </div><!-- /.modal-content -->
    </div><!-- /.modal-dialog -->
  </div><!-- /.modal -->

</template>

<template id="modal">
  <div>
    <div ref="modal" class="modal fade" tabindex="-1" role="dialog">

      <div class="modal-dialog modal-lg" role="document">
        <div class="modal-content">
          <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
            <h4 class="modal-title">Modal title</h4>
          </div>
          <div class="modal-body" style="height: 80vh">
            Stuff
            <input ref="input" type="text" class="form-control">
          </div>
          <div class="modal-footer">
            <button @click="showSubModal" type="button" class="btn btn-primary">Show Sub Modal</button>
          </div>
        </div><!-- /.modal-content -->
      </div><!-- /.modal-dialog -->
    </div><!-- /.modal -->
    <sub-modal ref="submodal"></sub-modal>
  </div>
</template>
Bolshevik answered 8/9, 2017 at 15:9 Comment(3)
Unfortunately the problem is still there.. as you can see here imgur.com/1D2CWwp, the scroll bar for #modal disappears after closing #submodal. you are then unable to scroll up to see the top of #modalMammiemammiferous
@Mammiemammiferous check it now, it should work. In general, I think opening a modal from another modal is a bad idea, but this should get you there.Bolshevik
@Mammiemammiferous There is some text added right before the example regarding modal-open.Bolshevik
T
0

Adding modal-open not solve "focus out of modal by tab" for me. After hours of researching only one thing that works for me is the most simple way: hide 1-st modal before show 2nd, and show 2nd after hide 1st.

$('#modal1').on('show.bs.modal', function () {
  $('#modal2').modal("hide");
})

$('#modal2').on('hide.bs.modal', function () {
  $('#modal1').modal("show");
})
Tomchay answered 19/10, 2023 at 0:39 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.