it-roy-ru.com

Twitter Bootstrap/jQuery - Как временно запретить закрытие модального окна?

Я использую модальные настройки Twitter Bootstrap с опциями по умолчанию, в которых вы можете щелкнуть фон или нажать [Esc], чтобы закрыть модал.

Тем не менее, когда я запускаю операцию ajax в модальном режиме, я хочу отключить модальное закрытие каким-либо образом. Поэтому я отключаю кнопки и скрываю кнопку закрытия модала, но не могу понять, как отключить фон и клавишу [Esc].

Я старался:

$('#myModal').modal({
    backdrop: 'static',
    keyboard: false
});

Но это, похоже, не работает на лету.

Мне также нужно будет снова включить фон и клавиатуру, как только закончится операция ajax.

34
BadHorsie

Примечание: это решение предназначено для Twitter начальной загрузки 2.x ! Смотрите этот ответ (чуть ниже) для различий в соответствии с начальной загрузкой 3.


Расширение модальной функциональности начальной загрузки без изменение исходного кода. 

Благодаря @David и его предложению на Как расширить плагин Twitter Bootstrap Я наконец-то заставил его работать. Это слегка модифицированная версия его решения с добавленной модальной блокировкой. Я публикую его как дополнительный ответ, так как думаю, что это может стать отправной точкой для других, кто, как я, усердно боролся с этой проблемой. 

// 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);

С помощью этой техники не должно быть необходимости изменять bootstrap.js, и те же функциональные возможности легче распределить между проектами начальной загрузки. Этот метод должен быть применим ко всем другим плагинам начальной загрузки. До сих пор пытался только с кнопкой, но я не могу понять, почему это не должно. 

см. рабочую скрипку -> http://jsfiddle.net/Sz7ZS/

17
davidkonrad

Есть более простой способ сделать это. Это запрос на загрузку начальной загрузки объясняет немного больше. Решение отключает все способы закрытия модальных (клавиатура, щелчок мышью, кнопка закрытия).

Все, что вам нужно сделать, чтобы отключить закрытие модального окна:

$('#myModal').data('bs.modal').isShown = false;

Чтобы снова включить закрытие:

$('#myModal').data('bs.modal').isShown = true;

Пример

Вот пример кода, который работает в сочетании с get jQuery:

// 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 );
});
10
qingu

Вы не единственный, кому не хватает этой функции. Я думаю, что bootstrap иногда слишком «минималистичен», у людей есть идея, что многое нужно сделать на «уровне реализации», но бесполезно, когда сами плагины jQuery для начальной загрузки делают это невозможным!

Вы должны реализовать эту функцию самостоятельно, например, так::

в bootstrap.js v2.1.1 модальный начинается в строке 61. 

в Modal.prototype, добавьте две функции, lock и unlock, чтобы это выглядело так (я показываю здесь только начало modal.prototype, потому что это слишком много кода)

  Modal.prototype = {

      constructor: Modal

      //add this function
    , lock: function () {
        this.options.locked = true
      }

      //add this function
    , unlock: function () {
        this.options.locked = false
      }

    , toggle: function () {
    ... 
    ...

Затем, также в Modal.prototype, найдите функцию hide и добавьте строку, чтобы она выглядела так (опять же, отображается только top of hide)

, hide: function (e) {
    e && e.preventDefault()

    var that = this

    //add this line
    if (that.options.locked) return

    e = $.Event('hide')
    ...
    ...

И наконец, измените $.fn.modal.defaults на:

  $.fn.modal.defaults = {
      backdrop: true
    , keyboard: true
    , show: true
    , locked: false //this line is added
  }

Теперь у вас есть функциональность блокировки/разблокировки на лету в вашей модальности начальной загрузки, предотвращая закрытие модального режима в критические моменты. 

Пример :

Это измененная версия "Live Demo" от 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">

Я вставил две кнопки «блокировать» и «разблокировать» - при нажатии они устанавливают модальный режим либо в заблокированном, либо в обычном режиме (настройки, с которыми он инициализируется)

Edit, в вашем случае вам просто нужно вызвать lock/onlock при выполнении ajax:

$("myModal").modal('lock');
$.ajax({
    url: url,
    ...
    ...
    , success(html) {
       ...
       ...
       $("#myModal").modal('unlock');
    }
});
9
davidkonrad

Вы можете создать переменную isBlocked, которую вы можете установить при вызове AJAX значения true, затем вы можете проверить ее на загрузочном событии bootrap модального вида следующим образом:

$('#myModal').on('hide.bs.modal', function (e) {
      //Prevent modal from closing is isBlocked is true
      if(isBlocked) return e.preventDefault();
})

Я думаю, что этот способ проще, чем расширение Bootsrap, надеюсь, он кому-нибудь поможет: D

8
alez

Спасибо @davidkonrad за вашу работу над этим. Я пытался реализовать это также, и кажется, что все изменилось с начальной загрузкой 3. Теперь вместо: 

_superModal.defaults 

теперь он присоединен к конструктору, поэтому вы должны сделать 

_superModal.Constructor.DEFAULTS

Также изменился конструктор, что означало, что я должен был скопировать и изменить его, что не идеально. Вместо этого я пришел к следующему коду, который работает и не копирует конструктор и должен быть более надежным, если в процессе загрузки начнутся изменения. Попробовать:

// 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);
    }
});

Итак, чтобы заблокировать модал:

$('#dlg').modal('lock');

и разблокировать:

$('#dlg').modal('unlock');

Ура!

6
ragamufin

Я обновил этот потрясающий скрипт для работы с Bootstrap 4 и 3 . Я сохраняю оба сценария, потому что у меня есть глобальный сценарий, который я использую в своих проектах, и некоторые из них используют обе версии Boostrap (не не волнуйтесь, один бутстрап на проект). Если у кого-то есть идея получше, поделитесь ею с нами.

// 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);
    }
});
0
Ezequiel Villarreal