IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Voir le flux RSS

Darkaurora

[Actualité] Étendre les fenêtres modals de Bootstrap.

Noter ce billet
par , 16/04/2015 à 09h42 (1007 Affichages)
Cela fait déjà longtemps que j'utilise ce composant proposé par le framework Bootstrap mais malheureusement il lui manquait une fonctionnalité intéressante: stackable.

Dans ces dernières versions la modal de BS permet justement de s'empiler mais malheureusement il y a un effet, que je trouve désagréable, c'est qu'a chaque ouverture d'une fenêtre un élément "backdrop" viens s'ajouter au DOM, rendant l'arrière plan de plus en plus opaque au fur et à mesure de l'ouverture de fenêtre.

Je me suis donc penché sur les solutions permettant de minimiser l'effet et deux solutions s'offrent à nous:
  • Soit nous définissons un code "rustine" qui permettra de corriger le problème.
  • Soit nous agissons directement au niveau du plugin.


La première solution est normalement celle que tout le monde devrait choisir, en effet les modals BS offrent une API permettant d'agir sur les éléments à 4 étapes de l'initialisation et de la destruction des fenêtres. De plus agir de cette manière nous assure l'intégrité du framework BS notamment lorsque décision est prise de le mettre à jour.

Malheureusement cette solution souffre d'un problème de "timing" dans notre cas d'étude, en agissant de cette manière et avec le même code que je propose ci dessous, vous constaterez des effets visuels indésirables.

C'est pourquoi j'ai opté pour la deuxième solution, un peu plus contraignante mais très fonctionnelle:

Après avoir lut le code du plugin deux fonctions nous intéressent:
Modal.prototype.show et Modal.prototype.backdrop.

Fonction Show:
Code javascript : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
Modal.prototype.show = function (_relatedTarget) {
    var that = this
    var e    = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
 
    this.$element.trigger(e)
 
    if (this.isShown || e.isDefaultPrevented()) return
 
    this.isShown = true
 
    this.checkScrollbar()
    this.setScrollbar()
    this.$body
      .addClass('modal-open')
// Nous intervenons ici afin d'enregistrer un nombre de modals ouvertes
      .data('modalIndice', this.$body.data('modalIndice') ?
        this.$body.data('modalIndice') + 1 :
        1
      )
 
    this.escape()
    this.resize()
 
    this.$element
      .on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
// Grace à la donnée modalIndice nous pouvons attribuer un numéro ordonné à la modal actuelle.
      .data('modalOrder', this.$body.data('modalIndice'))
 
    this.$dialog.on('mousedown.dismiss.bs.modal', function () {
      that.$element.one('mouseup.dismiss.bs.modal', function (e) {
        if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true
      })
    })
 
    this.backdrop(function () {
      var transition = $.support.transition && that.$element.hasClass('fade')
 
      if (!that.$element.parent().length) {
        that.$element.appendTo(that.$body) // don't move modals dom position
      }
 
      that.$element
        .show()
        .scrollTop(0)
 
      that.adjustDialog()
 
      if (transition) {
        that.$element[0].offsetWidth // force reflow
      }
 
      that.$element
        .addClass('in')
        .attr('aria-hidden', false)
 
      that.enforceFocus()
 
      var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
 
      transition ?
        that.$dialog // wait for modal to slide in
          .one('bsTransitionEnd', function () {
            that.$element.trigger('focus').trigger(e)
          })
          .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
        that.$element.trigger('focus').trigger(e)
    })
  }

Fonction Backdrop:
Code javascript : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
Modal.prototype.backdrop = function (callback) {
    var that = this
    var animate = this.$element.hasClass('fade') ? 'fade' : ''
    var lastVisibleBackdrop = this.$body.find('.modal-backdrop:last:visible') // Nous récupérons le dernier arrière plan visible
    var lastHiddenBackdrop = this.$body.find('.modal-backdrop:hidden:last') // Nous récupérons le dernier arrière plan caché
 
    if (this.isShown && this.options.backdrop) {
      var doAnimate = $.support.transition && animate
// On cache tous les autres backdrop à part celui actif puis on réduis le z-index des autres modals pour les faire passer sous le backdrop actif
      if (lastVisibleBackdrop.length) {
        lastVisibleBackdrop.hide();
        this.$body.find('.modal:visible').each(function() {
          if ($(this)[0] !== that.$element[0]) {
            $(this).css('z-index', 1030)
          }
        })
      }
 
      this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
          .appendTo(this.$body)
 
      this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
        if (this.ignoreBackdropClick) {
          this.ignoreBackdropClick = false
          return
        }
        if (e.target !== e.currentTarget) return
        this.options.backdrop == 'static'
          ? this.$element[0].focus()
          : this.hide()
      }, this))
 
      if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
 
      this.$backdrop.addClass('in')
 
      if (!callback) return
 
      doAnimate ?
        this.$backdrop
          .one('bsTransitionEnd', callback)
          .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
        callback()
 
    } else if (!this.isShown && this.$backdrop) {
      this.$backdrop.removeClass('in')
// On affiche le dernier backdrop caché puis on augmente le z-index de la modal ayant l'indice juste au dessous de celle qui est active et en cours de destruction
      if (lastHiddenBackdrop.length) {
        lastHiddenBackdrop.show();
        this.$body.find('.modal:visible').each(function() {
          if ($(this).data('modalOrder') === that.$element.data('modalOrder') - 1) {
            $(this).css('z-index', 1050)
          }
        })
      }
 
      var callbackRemove = function () {
        that.removeBackdrop()
        callback && callback()
      }
      $.support.transition && this.$element.hasClass('fade') ?
        this.$backdrop
          .one('bsTransitionEnd', callbackRemove)
          .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
        callbackRemove()
 
    } else if (callback) {
      callback()
    }
  }

Et le tour est joué . Certes il reste encore pas mal de travail afin de fournir un plugin plus fournis que l'original et ainsi s'en détaché, mais l'idée est là et elle n'est pas bien compliqué.

Avis, remarques et optimisations sont les bienvenus

Le code est disponible ici

Envoyer le billet « Étendre les fenêtres modals de Bootstrap. » dans le blog Viadeo Envoyer le billet « Étendre les fenêtres modals de Bootstrap. » dans le blog Twitter Envoyer le billet « Étendre les fenêtres modals de Bootstrap. » dans le blog Google Envoyer le billet « Étendre les fenêtres modals de Bootstrap. » dans le blog Facebook Envoyer le billet « Étendre les fenêtres modals de Bootstrap. » dans le blog Digg Envoyer le billet « Étendre les fenêtres modals de Bootstrap. » dans le blog Delicious Envoyer le billet « Étendre les fenêtres modals de Bootstrap. » dans le blog MySpace Envoyer le billet « Étendre les fenêtres modals de Bootstrap. » dans le blog Yahoo

Mis à jour 17/04/2015 à 11h33 par Bovino

Catégories
Javascript , Développement Web

Commentaires