web-dev-qa-db-fra.com

Twitter Bootstrap/jQuery - Comment empêcher temporairement la fermeture du modal?

J'utilise les modaux Twitter Bootstrap, avec les options par défaut permettant de cliquer sur le fond ou d'appuyer sur [Echap] pour fermer le modal.

Cependant, lorsque je lance une opération ajax dans le modal, je souhaite désactiver la fermeture du modal. Donc, je désactive les boutons et masque le bouton de fermeture du modal, mais je ne vois pas comment désactiver le fond et la touche [Échap].

J'ai essayé:

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

Mais cela ne semble pas fonctionner à la volée.

Je devrai également réactiver le fond et le clavier une fois l'opération ajax terminée.

34
BadHorsie

Note: cette solution cible Twitter bootstrap 2.x ! Voir cette réponse (juste en dessous) pour connaître les différences selon bootstrap 3.


Extension de la fonctionnalité modale d'amorçage sans modification de la source d'origine. 

Merci à @David et à sa suggestion sur Comment étendre le plug-in Bootstrap de Twitter Je l’ai enfin fait fonctionner. C'est une version légèrement modifiée de sa solution avec "modal" lock ajouté. Je l’affiche comme réponse supplémentaire car je pense que cela pourrait être un point de départ pour d’autres qui, comme moi, ont eu beaucoup de mal à lutter contre ce problème. 

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

Avec cette technique, il ne devrait pas être nécessaire de modifier bootstrap.js, et la même fonctionnalité peut être plus facilement partagée entre les projets bootstrap. Cette méthode devrait être applicable à tous les autres plugins d'amorçage. Je n'ai pour l'instant essayé qu'avec un bouton, mais je ne vois pas pourquoi il ne devrait pas. 

voir travail de violon -> http://jsfiddle.net/Sz7ZS/

17
davidkonrad

Il y a un moyen plus facile de le faire. Cette demande d'amorçage/bootstrap explique un peu plus. La solution désactive toutes les méthodes de fermeture du modal (clavier, clic de souris, bouton de fermeture).

Tout ce que vous avez à faire pour désactiver la fermeture du modal est:

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

Pour activer à nouveau la fermeture:

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

Exemple

Voici un exemple de code qui fonctionne conjointement avec un objet 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

Vous n'êtes pas le seul à ne pas avoir cette fonctionnalité. Je pense que parfois bootstrap est trop "minimaliste", les personnes derrière ont l’idée que beaucoup devrait être fait dans la "couche d’implémentation", mais il est inutile si les plugins bootstrap jQuery le rendent impossible!

Vous devez implémenter la fonctionnalité vous-même, comme ceci:

dans bootstrap.js v2.1.1 modal commence à la ligne 61. 

dans Modal.prototype, ajoutez deux fonctions, lock et unlock, donc cela ressemble à ceci (je montre ici seulement le début de modal.prototype, parce que c'est trop de 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 () {
    ... 
    ...

Ensuite, également dans Modal.prototype, recherchez la fonction hide et ajoutez une ligne pour qu'elle ressemble à ceci (là encore, seul le haut de la page masquée est affiché)

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

    var that = this

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

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

Et enfin, remplacez $.fn.modal.defaults par:

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

Vous disposez maintenant d'une fonctionnalité de verrouillage/déverrouillage à la volée dans votre modal d'amorçage, ce qui empêche l'utilisateur de fermer le modal à des moments critiques. 

Exemple :

Ceci est une version modifiée de "Live Demo" de 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">

J'ai inséré deux boutons, "verrouiller" et "déverrouiller" - lorsque l'utilisateur clique dessus, le modal est activé en mode verrouillé ou normal (les paramètres avec lesquels il est initialisé)

Edit, dans votre cas, il vous suffit d'appeler lock/onlock lorsque vous utilisez ajax:

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

Vous pouvez créer une variable isBlocked que vous pouvez définir en effectuant des appels AJAX à true, puis vous pouvez la vérifier sur l'événement de masquage de mode bootrap modal de la manière suivante:

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

Je pense que cette façon de faire est plus facile que d'étendre Bootsrap, j'espère que cela aidera quelqu'un: D

8
alez

J'ai mis à jour ce script génial pour qu'il fonctionne avec Bootstrap 4 & 3 . Je garde les deux scénarios parce que j'ai un script global que j'utilise dans mes projets et pas de souci, un bootstrap par projet). Si quelqu'un a une meilleure idée, partagez-la avec nous.

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