23

Sometimes when I want to quickly select the entire text of an input (within a modal), I begin selecting from the end of the text and move the mouse to the left until the entire text is selected and then I release.

Sometimes this release will occur outside the modal because the mouse movement is fast.

Picture describing the movement:

Picture describing the release

The problem is that the modal is closed when I release outside.

Question: how can I prevent the modal from closing when releasing outside?

I'm okay with the modal being closed with a click outside. But not okay with the release event.

I'm using:

  • angularjs 1.5.8
  • angular-bootstrap 2.5.0 (aka bootstrap-ui)
  • bootstrap 3.3.7 (only css!!! not js, because js is provided by the above)

Update: I've created a plunkr and a GIF: https://plnkr.co/edit/mxDLAdnrQ4p0KKyw?p=info

<div class="modal-body">
  <div class="form-group">
    <label for="">Foo</label>
    <input type="text" class="form-control input-sm" ng-model="foo">

    <p>Do this: select the text from right to left and release the mouse outside the modal.</p>
  </div>
</div>

GIF:

the modal is closed when released outside

Update 2

I have new information! This started happening after the last Goole Chrome update! I tried with another computer that had the previous version of Chrome and the modal doesn't close.

sports
  • 7,851
  • 14
  • 72
  • 129
  • its strange that it is happening in the first because its not the default behaviour. If you select text in an input on a bootstrap modal and release click outside the modal it does not close – Fakhar Ahmad Rasul Apr 17 '19 at 18:00
  • In fact this started happening no long ago. I will try to create a plunkr to confirm... – sports Apr 17 '19 at 19:00
  • It looks like it's a side effect of the way they have implemented the "click outside" behavior for dismissing a modal. They are watching for mouse up. You can confirm this by clicking and holding outside the modal - it doesn't dismiss until you release the mouse. It's interesting that you can't get the opposite behavior (i.e. click outside the modal, drag until you are inside the modal, and then release the mouse). – Lex Apr 17 '19 at 19:47
  • I added new information to the post. This problem is related to the new Chrome update!! Before the update this wasn't the behavior. – sports Apr 17 '19 at 19:54

10 Answers10

3
//prevent modal close when click starts in modal and ends on backdrop


$(document).on('mousedown', '.modal', function(e){      
    window.clickStartedInModal = $(e.target).is('.modal-dialog *');     
});

$(document).on('mouseup', '.modal', function(e){
    if(!$(e.target).is('.modal-dialog *') && window.clickStartedInModal) {
        window.preventModalClose = true;
    }           
});

$("#modal").on("hide.bs.modal", function (e) {
    if(window.preventModalClose){
        window.preventModalClose = false;
        return false;
    }     
}); 
Negaal
  • 41
  • 2
3

The original repository has been archived and no contributions are accepted.

I forked a version and added my fixes for those who are interested:
https://github.com/peteriman/bootstrap

The comparison below:
https://github.com/angular-ui/bootstrap/compare/master...peteriman:modal-patch

=  // moved from template to fix issue #2280
-  element.on('click', scope.close);
+  var ignoreClick = false;
+  element.on('mousedown', function(evt1) {
+    element.one('mouseup', function(evt2) {
+      if (evt1.target !== evt2.target)
+        ignoreClick = true;
+    });
+  });
+  element.on('click', function(){
+    if (ignoreClick) ignoreClick = false;
+    else scope.close.apply(this, arguments);
+  });

As mousedown and mouseup events trigger before click event, the code checks if mousedown and mouseup are on the same element. If on different elements, it sets ignoreClick=true for the click event to not trigger.

Maintains backward compatibility for click event for existing codes that calls element.click() programmatically.

Original problem:
https://plnkr.co/edit/mxDLAdnrQ4p0KKyw?p=info&preview

Solution by me: (plkr, modal.js, line 103-114)
https://plnkr.co/edit/V42G9NcTUnH9n9M4?p=info&preview

2

I updated only the code referring to "Modal.js" in bootstrap.js and bootstrap.min.js

Corrected version:

 * Bootstrap: modal.js v3.4.1
 * https://getbootstrap.com/docs/3.4/javascript/#modals

bootstrap.js print

  • hm? i don't get it. – sports May 08 '19 at 21:54
  • 1
    You are using the bootstrap version 3.3.7. In version 3.4.1 has been fixed. In my project I could not update the entire bootstrap. Then manually updated only part of the code of "Modal". – Carlos Sitolino May 10 '19 at 12:39
  • Also please notice something in the plnkr I posted: I'm not even using `dist/js/bootstrap.js`. I'm using only `dist/css/bootstrap.css` and `ui-bootstrap.min.js` – sports May 10 '19 at 21:45
  • 1
    I was able to do the same for ui-bootstrap.min.js. You use the latest version 2.5.0, I overwritten the modal in script.js with the fix.https://plnkr.co/edit/RmyBk0Mrw3aqp8QSFzcu – Carlos Sitolino May 13 '19 at 12:49
  • but can you highlight the fix itself? maybe I could submit it to the official git – sports May 13 '19 at 17:32
  • There is a Pull Request, but was not accepted yet.https://github.com/angular-ui/bootstrap/pull/6640 – Carlos Sitolino May 13 '19 at 19:50
2

Yes, this started happening again after the last Goole Chrome update Version 74.0.3729.169, is this a bug with Chrome we can't fix and that we'll just have to wait for a Chrome update for it to be resolved?

or a bootstrap maintainer will update the code for fixing this?

Issue url: https://github.com/twbs/bootstrap/issues/28844

Akash Vishwakarma
  • 177
  • 1
  • 2
  • 11
2

This problem is not recent is already mentioned on github

https://github.com/angular-ui/bootstrap/issues/5810

the following solution works very well with small improvements if necessary.

 $rootScope.$watch(() => document.querySelectorAll('.modal').length, val => { 
  //everytime the number of modals changes
 for (let modal of document.querySelectorAll('.modal')) {
    if ($uibModalStack.getTop().value.backdrop !== 'static') { // Testing if the 
    modal is supposed to be static before attaching the event
      modal.addEventListener('mousedown', e => {
        if (e.which === 1) {
          $uibModalStack.getTop().key.dismiss()
      }
    })
       modal.querySelector('.modal-content').addEventListener('mousedown', e => {
        e.stopPropagation()
    })
   }
  }
  if (val > 0) {
   $uibModalStack.getTop().value.backdrop = 'static'
  }
})

Another solution on the same principle that keeps the draggrable footer and header of the modal

  $rootScope.$watch(function () {
    return $document.find('.modal').length;
     }, function (val) {
    if(openedWindows.top() ) {
      var  modal = $document.find('.modal');
      angular.forEach(modal, function(value) {
        if ($modalStack.getTop().value.backdrop !== 'static') {
          value.addEventListener('mousedown', function (e) {
            if (value === e.target && e.which === 1 && openedWindows.top()) {
               $modalStack.getTop().key.dismiss();
            }
          });
        }
      });
      if (val>0) {
        $modalStack.getTop().value.backdrop = 'static';
      }
    }
  });
Greg-A
  • 772
  • 1
  • 19
  • 41
1

I'm using Bootstrap v3.0.0 and ran into the same problem. In the end, I had to change a click event to a mousedown event.

In my bootstrap.js file, under the modal.js section, I changed this.$element.on('click.dismiss.modal', $.proxy(function (e) to this.$element.on('mousedown.dismiss.modal', $.proxy(function (e). and everything appears to be working. You may also have to change this in the bootstrap.min.js file.

Note, this will immediately close the modal on mouse down of backdrop so if for some reason you want a user to be able to click down on the backdrop, then drag the mouse and release on the modal, this will not work.

0

Have you tried using backdrop: 'static'. I think that should do the trick. It is present in the documentation here

  • 1
    That works but then the modal is not closed with a single click outside of it. In my original post I said: "I'm okay with the modal being closed with a click outside. But not okay with the release event." -- if I don't find a solution soon then I will need to opt for this one :( – sports Apr 25 '19 at 16:43
0

Add css padding around modal window and resize it larger. Click outside still works but releasing mouse while dragging over the edge won't close it.

mrtvrt
  • 1
0

I had a similar situation with range slider. leaving click during slide outside the modal closes it. so I removed data-toggle="modal" and data-target="#mymodal" and added a click event with extra parameters

jQuery('button#modal_toggler').click(function(){
  jQuery('#myModal').modal({
          backdrop: 'static',
          keyboard: false
  })
})

backdrop to disable modal close on clicking outside keyboard this is for my scenario, to disable keyboard entry for closing modal

ealgehdr
  • 43
  • 12
0

I have figured out different way to solve the problem, idk if it will cause a problem later but anyway it works, so basically, I put modal-dialog to another <div> object (I call it modal-helper) and then put it to modal. The modal-helper element width and height are inherited (100%) as default but there is small space on top so you can use some margin and padding to close it.

<div class="modal fade" id="login-modal" tabindex="-1" aria-labelledby="loginModalLabel" style="display: none;" aria-hidden="true">
     <div id="modal-helper" style="pointer-events: auto;">
          <div class="modal-dialog">
               ...
          </div>
     </div>
</div>

Then I have used some JS to hide modal when modal-helper (as backdrop) is clicked (by the 'clicked' I mean when pointerup event triggered after pointerdown event on modal-helper).

The code below sets the value of isPointerDownToModalHelper true when pointerdown event triggered on modal-helper, then when the pointerup event triggered on any object it hides the modal and sets the value of isPointerDownToModalHelper back to false:

var login_modal_helper = document.getElementById('modal-helper')
var isPointerDownToModalHelper = false;
addEventListener('pointerdown', (event) => {
     var objectID = event['path']['0']['id'];

     if (objectID === login_modal_helper.id) { // if pointer was over modal-helper
          isPointerDownToModalHelper = true;
     }

});

addEventListener('pointerup', (event) => {

     if (isPointerDownToModalHelper === true) {
          isPointerDownToModalHelper = false;
          $('#login-modal').modal('hide'); // hide the modal
     }

});

It seems to work fine for now, I hope it can help someone :).

Memduh Yılmaz
  • 325
  • 2
  • 8