问题
I am using Twitter Bootstrap modals, with the default options where you can click the backdrop or press [Esc] to close the modal.
However, when I initiate an ajax operation in the modal I want to disable the modal from being closed in any way. So I disable buttons and hide the modal's close button but I can't figure out how to disable the backdrop and the [Esc] key.
I tried:
$('#myModal').modal({
backdrop: 'static',
keyboard: false
});
But this doesn't seem to work on the fly.
I will also need to re-enable the backdrop and keyboard once the ajax operation is finished.
回答1:
Note: This solution is targeting twitter bootstrap 2.x! See this answer (just below) for differences according to bootstrap 3.
Extending bootstrap modal functionality without modifying original source.
Thanks to @David and his suggestion at How to Extend Twitter Bootstrap Plugin I finally got it to work. It is a slightly modified version of his solution with modal "lock" added. I post it as a additional answer since I think it may could be a starting point for others that like me have struggled hard with this issue.
// save the original function object
var _superModal = $.fn.modal;
// add locked as a new option
$.extend( _superModal.defaults, {
locked: false
});
// create a new constructor
var Modal = function(element, options) {
_superModal.Constructor.apply( this, arguments )
}
// extend prototype and add a super function
Modal.prototype = $.extend({}, _superModal.Constructor.prototype, {
constructor: Modal
, _super: function() {
var args = $.makeArray(arguments)
// call bootstrap core
_superModal.Constructor.prototype[args.shift()].apply(this, args)
}
, lock : function() {
this.options.locked = true
}
, unlock : function() {
this.options.locked = false
}
, hide: function() {
if (this.options.locked) return
this._super('hide')
}
});
// override the old initialization with the new constructor
$.fn.modal = $.extend(function(option) {
var args = $.makeArray(arguments),
option = args.shift()
// this is executed everytime element.modal() is called
return this.each(function() {
var $this = $(this)
var data = $this.data('modal'),
options = $.extend({}, _superModal.defaults, $this.data(), typeof option == 'object' && option)
if (!data) {
$this.data('modal', (data = new Modal(this, options)))
}
if (typeof option == 'string') {
data[option].apply( data, args )
}
});
}, $.fn.modal);
With this technique it should not be nessecary to alter bootstrap.js, and the same functionality can more easily be shared among bootstrap projects. This method should be applicable to all the other bootstrap plugins. Have so far only tried with button though, but I cant se why it shouldnt.
see working fiddle -> http://jsfiddle.net/Sz7ZS/
回答2:
There is an easier way to do it. This bootstrap pull request explains a little more. The solution disables all methods for closing the modal (keyboard, mouse click, close button).
All you have to do to disable closing the modal is:
$('#myModal').data('bs.modal').isShown = false;
To enable closing again:
$('#myModal').data('bs.modal').isShown = true;
Example
Here is some sample code that works in conjunction with a jQuery get:
// disable closing the modal
$('#myModal').data('bs.modal').isShown = false;
// Send an HTTP GET request to the server - replace this with getJSON, post or ajax as needed
$.get( "ajax/test.html", function( data ) {
// enable closing the modal
$('#myModal').data('bs.modal').isShown = true;
// Do something with the data
$( ".result" ).html( data );
});
回答3:
You are not the only one who are missing that feature. I think bootstrap sometimes is too "minimalistic", the people behind has the idea a lot should be done in the "implementation layer", but it is to no use when the bootstrap jQuery plugins themselves makes it impossible!
You have to implement the functionality yourself, like this :
in bootstrap.js
v2.1.1 modal begins at line 61.
in Modal.prototype
, add two functions, lock
and unlock
, so it looks like this (I show here only the beginning of modal.prototype
, becuase it is too much code)
Modal.prototype = {
constructor: Modal
//add this function
, lock: function () {
this.options.locked = true
}
//add this function
, unlock: function () {
this.options.locked = false
}
, toggle: function () {
...
...
Then, also in Modal.prototype, find the function hide
, and add a line so it looks like this (again, only top of hide is showed)
, hide: function (e) {
e && e.preventDefault()
var that = this
//add this line
if (that.options.locked) return
e = $.Event('hide')
...
...
And finally, alter $.fn.modal.defaults
to :
$.fn.modal.defaults = {
backdrop: true
, keyboard: true
, show: true
, locked: false //this line is added
}
Now you have on-the-fly lock/unlock functionality in your bootstrap modal, preventing the user from closing the modal at critical moments.
Example :
This is an altered version of "Live Demo" from http://twitter.github.com/bootstrap/javascript.html#modals
<!-- Button to trigger modal -->
<a href="#myModal" role="button" class="btn" data-toggle="modal">Launch demo modal</a>
<!-- Modal -->
<div id="myModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="myModalLabel">Modal header</h3>
</div>
<div class="modal-body">
<p>One fine body…</p>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
<button class="btn btn-primary" onclick="$('#myModal').modal('lock');">lock</button>
<button class="btn btn-primary" onclick="$('#myModal').modal('unlock');">unLock</button>
</div>
</div>
<script type="text/javascript">
I have inserted two buttons, "lock" and "unlock" - when clicked, they set the modal in either locked or normal mode (the settings it is initialised with)
Edit, in your case, you just have to call lock/onlock when doing ajax :
$("myModal").modal('lock');
$.ajax({
url: url,
...
...
, success(html) {
...
...
$("#myModal").modal('unlock');
}
});
回答4:
You can create a variable isBlocked that you can set while doing AJAX calls to true, then you can check it on bootsrap modal hide event this way:
$('#myModal').on('hide.bs.modal', function (e) {
//Prevent modal from closing is isBlocked is true
if(isBlocked) return e.preventDefault();
})
I think this way is easier than extending Bootsrap, hope it helps someone :D
回答5:
Thanks @davidkonrad for your work on this. I was trying to implement this as well and it seems that things have changed with bootstrap 3. Now instead of:
_superModal.defaults
it is now attached to the constructor so you have to do
_superModal.Constructor.DEFAULTS
Also the constructor has changed which meant that I had to copy it and modify it which is less than ideal. Instead I've come up with the following code that works and does not copy the constructor and should be more fool-proof if bootstrap changes going forward. Have a go:
// save the original function object
var _superModal = $.fn.modal;
// add locked as a new option
$.extend( _superModal.Constructor.DEFAULTS, {
locked: false
});
// capture the original hide
var _hide = _superModal.Constructor.prototype.hide;
// console.log('HIDE:', _hide);
// add the lock, unlock and override the hide of modal
$.extend(_superModal.Constructor.prototype, {
// locks the dialog so that it cannot be hidden
lock: function() {
// console.log('lock called');
// console.log('OPTIONS',this.options);
this.options.locked = true;
}
// unlocks the dialog so that it can be hidden by 'esc' or clicking on the backdrop (if not static)
,unlock: function() {
// console.log('unlock called');
this.options.locked = false;
}
// override the original hide so that the original is only called if the modal is unlocked
,hide: function() {
// console.log('hide called');
if (this.options.locked) return;
_hide.apply(this, arguments);
}
});
So in order to lock the modal:
$('#dlg').modal('lock');
and to unlock:
$('#dlg').modal('unlock');
Yay!
回答6:
I updated this awesome script to work with Bootstrap 4 & 3. I'm keeping both scenarios because I have a global script that I'm using in my projects, and some of them are using both version of Boostrap (don't worry, one bootstrap per project). If anyone has a better idea, please share it with us.
// save the original function object
var _superModal = $.fn.modal;
// Bootstrap 3: Constructor.DEFAULTS
// Bootstrap 4: Constructor.Default
var _superModalDefault = (typeof _superModal.Constructor.DEFAULTS === 'undefined') ? _superModal.Constructor.Default : _superModal.Constructor.DEFAULTS;
// add locked as a new option
$.extend(_superModalDefault, {
locked: false
});
// capture the original hide
var _hide = _superModal.Constructor.prototype.hide;
// console.log('HIDE:', _hide);
// add the lock, unlock and override the hide of modal
$.extend(_superModal.Constructor.prototype, {
// locks the dialog so that it cannot be hidden
// Bootstrap 3: this.options
// Bootstrap 4: this._config
lock: function() {
// console.log('lock called');
if (this.options)
this.options.locked = true; // Bootstrap 3
else
this._config.locked = true; // Bootstrap 4
}
// unlocks the dialog so that it can be hidden by 'esc' or clicking on the backdrop (if not static)
,unlock: function() {
// console.log('unlock called');
if (this.options)
this.options.locked = false; // Bootstrap 3
else
this._config.locked = false; // Bootstrap 4
}
// override the original hide so that the original is only called if the modal is unlocked
,hide: function() {
// console.log('hide called');
if (this.options)
if (this.options.locked) return; // Bootstrap 3
else
if (this._config.locked) return; // Bootstrap 4
_hide.apply(this, arguments);
}
});
来源:https://stackoverflow.com/questions/13421750/twitter-bootstrap-jquery-how-to-temporarily-prevent-the-modal-from-being-clo