web-dev-qa-db-fra.com

Comment puis-je fermer un Twitter Bootstrap popover avec un clic de n'importe où sur la page?

J'utilise actuellement Popovers avec Twitter Bootstrap, lancé comme ceci:

$('.popup-marker').popover({
        html: true,
        trigger: 'manual'
    }).click(function(e) {
        $(this).popover('toggle');
        e.preventDefault();
    });

Comme vous pouvez le voir, ils sont déclenchés manuellement et en cliquant sur .popup-marker (qui est un div avec une image d'arrière-plan) fait basculer un popover. Cela fonctionne très bien, mais j'aimerais aussi pouvoir fermer le popover d'un clic n'importe où ailleurs sur la page (mais pas sur le popover lui-même!).

J'ai essayé différentes choses, notamment les suivantes, mais aucun résultat n'a été démontré:

$('body').click(function(e) {
    $('.popup-marker').popover('hide');
});

Comment puis-je fermer la popover avec un clic n'importe où ailleurs sur la page, mais pas avec un clic sur la popover elle-même?

153
Travis Northcutt

En supposant qu’un seul popover puisse être visible à tout moment, vous pouvez utiliser un ensemble d’indicateurs pour indiquer quand un popover est visible, puis seulement les masquer.

Si vous définissez l'écouteur d'événements sur le corps du document, il se déclenchera lorsque vous cliquerez sur l'élément marqué par 'popup-marker'. Vous devrez donc appeler stopPropagation() sur l'objet événement. Et appliquez la même astuce en cliquant sur le popover lui-même.

Vous trouverez ci-dessous un code JavaScript fonctionnel qui le fait. Il utilise jQuery> = 1.7

jQuery(function() {
    var isVisible = false;

    var hideAllPopovers = function() {
       $('.popup-marker').each(function() {
            $(this).popover('hide');
        });  
    };

    $('.popup-marker').popover({
        html: true,
        trigger: 'manual'
    }).on('click', function(e) {
        // if any other popovers are visible, hide them
        if(isVisible) {
            hideAllPopovers();
        }

        $(this).popover('show');

        // handle clicking on the popover itself
        $('.popover').off('click').on('click', function(e) {
            e.stopPropagation(); // prevent event for bubbling up => will not get caught with document.onclick
        });

        isVisible = true;
        e.stopPropagation();
    });


    $(document).on('click', function(e) {
        hideAllPopovers();
        isVisible = false;
    });
});

http://jsfiddle.net/AFffL/539/

Le seul inconvénient est que vous ne pourrez pas ouvrir 2 popovers en même temps. Mais je pense que ce serait déroutant pour l'utilisateur, de toute façon :-)

100
Radu Cugut

C'est encore plus facile:

$('html').click(function(e) {
    $('.popup-marker').popover('hide');
});

$('.popup-marker').popover({
    html: true,
    trigger: 'manual'
}).click(function(e) {
    $(this).popover('toggle');
    e.stopPropagation();
});
73
prbaron

J'avais un besoin similaire et j'ai trouvé ceci grande petite extension de Twitter Bootstrap Popover de Lee Carmichael, appelée BootstrapX - clickover . Il a aussi quelques exemples d'utilisation ici . Fondamentalement, cela changera la popover en un composant interactif qui se fermera lorsque vous cliquerez ailleurs sur la page, ou sur un bouton de fermeture dans la popover. Cela permettra également d'ouvrir plusieurs popovers à la fois et de nombreuses autres fonctionnalités de Nice.

Le plugin peut être trouvé ici .

Exemple d'utilisation

<button rel="clickover" data-content="Show something here. 
    <button data-dismiss='clickover'
    >Close Clickover</button>"
>Show clickover</button>

javascript:

// load click overs using 'rel' attribute
$('[rel="clickover"]').clickover();
48
Miika L.

La solution acceptée m'a donné quelques problèmes (en cliquant sur l'élément '.popup-marker' de la fenêtre ouverte, les popovers ne fonctionnaient plus après). Je suis venu avec cette autre solution qui fonctionne parfaitement pour moi et qui est assez simple (j'utilise Bootstrap 2.3.1):

$('.popup-marker').popover({
    html: true,
    trigger: 'manual'
}).click(function(e) {
    $('.popup-marker').not(this).popover('hide');
    $(this).popover('toggle');
});
$(document).click(function(e) {
    if (!$(e.target).is('.popup-marker, .popover-title, .popover-content')) {
        $('.popup-marker').popover('hide');
    }
});

UPDATE: Ce code fonctionne également avec Bootstrap 3!

37
RayOnAir

lire "Dismiss on next click" ici http://getbootstrap.com/javascript/#popovers

Vous pouvez utiliser le déclencheur de focus pour ignorer les occurrences lors du clic suivant, mais vous devez utiliser la balise <a>, et non la balise <button>, et vous devez également inclure un attribut tabindex. ..

Exemple:

<a href="#" tabindex="0" class="btn btn-lg btn-danger"
  data-toggle="popover" data-trigger="focus" title="Dismissible popover"
  data-content="And here's some amazing content. It's very engaging. Right?">
  Dismissible popover
</a>
19
Andrej Sramko

Toutes les réponses existantes sont assez faibles, car elles reposent sur la capture des événements all document, puis la recherche de popovers actifs ou la modification de l'appel en .popover().

Une meilleure approche consiste à écouter les événements show.bs.popover sur le corps du document, puis à réagir en conséquence. Le code ci-dessous ferme les fenêtres lorsque le document est cliqué ou esc est appuyé, niquement écouteurs de liaison d'événements lorsque des popovers sont affichés:

function closePopoversOnDocumentEvents() {
  var visiblePopovers = [];

  var $body = $("body");

  function hideVisiblePopovers() {
    $.each(visiblePopovers, function() {
      $(this).popover("hide");
    });
  }

  function onBodyClick(event) {
    if (event.isDefaultPrevented())
      return;

    var $target = $(event.target);
    if ($target.data("bs.popover"))
      return;

    if ($target.parents(".popover").length)
      return;

    hideVisiblePopovers();
  }

  function onBodyKeyup(event) {
    if (event.isDefaultPrevented())
      return;

    if (event.keyCode != 27) // esc
      return;

    hideVisiblePopovers();
    event.preventDefault();
  }

  function onPopoverShow(event) {
    if (!visiblePopovers.length) {
      $body.on("click", onBodyClick);
      $body.on("keyup", onBodyKeyup);
    }
    visiblePopovers.Push(event.target);
  }

  function onPopoverHide(event) {
    var target = event.target;
    var index = visiblePopovers.indexOf(target);
    if (index > -1) {
      visiblePopovers.splice(index, 1);
    }
    if (visiblePopovers.length == 0) {
      $body.off("click", onBodyClick);
      $body.off("keyup", onBodyKeyup);
    }
  }

  $body.on("show.bs.popover", onPopoverShow);
  $body.on("hide.bs.popover", onPopoverHide);
}
7
David Wolever

https://github.com/lecar-red/bootstrapx-clickover

C'est une extension de Twitter bootstrap popover et résoudra le problème très simplement.

5
Ibrahim

Je donne à tous mes ancres popovers la classe activate_popover. Je les active tous à la fois en charge

$('body').popover({selector: '.activate-popover', html : true, container: 'body'})

pour utiliser la fonctionnalité click away que j'utilise (en script café):

$(document).on('click', (e) ->
  clickedOnActivate = ($(e.target).parents().hasClass("activate-popover") || $(e.target).hasClass("activate-popover"))
  clickedAway = !($(e.target).parents().hasClass("popover") || $(e.target).hasClass("popover"))
if clickedAway && !clickedOnActivate
  $(".popover.in").prev().popover('hide')
if clickedOnActivate 
  $(".popover.in").prev().each () ->
    if !$(this).is($(e.target).closest('.activate-popover'))
      $(this).popover('hide')
)

Ce qui fonctionne parfaitement avec bootstrap 2.3.1

2

Même s'il y a beaucoup de solutions ici, je voudrais proposer la mienne aussi, je ne sais pas s'il existe une solution qui résout tout, mais j'ai essayé 3 d'entre elles et ils ont eu des problèmes, comme en cliquant sur le popover, il le cache, d’autres que si j’avais un autre bouton de popover sur lequel cliquer, les deux/plusieurs popovers apparaîtront toujours (comme dans la solution sélectionnée), comment,

var curr_popover_btn = null;
// Hide popovers function
function hide_popovers(e)
{
    var container = $(".popover.in");
    if (!container.is(e.target) // if the target of the click isn't the container...
        && container.has(e.target).length === 0) // ... nor a descendant of the container
    {
        if( curr_popover_btn != null )
        {
            $(curr_popover_btn).popover('hide');
            curr_popover_btn = null;
        }
        container.hide();
    }
}
// Hide popovers when out of focus
$('html').click(function(e) {
    hide_popovers(e);
});
$('.popover-marker').popover({
    trigger: 'manual'
}).click(function(e) {
    hide_popovers(e);
    var $popover_btns = $('.popover-marker');
    curr_popover_btn = this;
    var $other_popover_btns = jQuery.grep($($popover_btns), function(popover_btn){
                return ( popover_btn !== curr_popover_btn );
            });
    $($other_popover_btns).popover('hide');
    $(this).popover('toggle');
    e.stopPropagation();
});
2
Roshdy

Pour une raison quelconque, aucune des autres solutions ici ne fonctionnait pour moi. Cependant, après beaucoup de dépannage, je suis finalement arrivé à cette méthode qui fonctionne parfaitement (du moins pour moi).

$('html').click(function(e) {
  if( !$(e.target).parents().hasClass('popover') ) {
    $('#popover_parent').popover('destroy');
  }
});

Dans mon cas, j’ajoutais un popover à une table et le positionnais absolument au-dessus/au-dessous de la td sur laquelle on avait cliqué. La sélection de la table a été gérée par jQuery-UI Selectable, donc je ne suis pas sûr que cela gênait. Cependant, chaque fois que je cliquais dans la fenêtre contextuelle, mon gestionnaire de clics qui ciblait $('.popover') ne fonctionnait jamais et la gestion des événements était toujours déléguée au gestionnaire de clics $(html). Je suis assez nouveau chez JS, alors peut-être qu'il me manque quelque chose?

Quoi qu'il en soit, j'espère que cela aide quelqu'un!

2
moollaza

Je mettrais l'accent sur le pop-over nouvellement créé et le supprimerais sur flou. De cette façon, il n'est pas nécessaire de vérifier quel élément du DOM a été cliqué et la fenêtre contextuelle peut être cliquée et sélectionnée: elle ne perdra pas son focus et ne disparaîtra pas.

Le code:

    $('.popup-marker').popover({
       html: true,
       trigger: 'manual'
    }).click(function(e) {
       $(this).popover('toggle');
       // set the focus on the popover itself 
       jQuery(".popover").attr("tabindex",-1).focus();
       e.preventDefault();
    });

    // live event, will delete the popover by clicking any part of the page
    $('body').on('blur','.popover',function(){
       $('.popup-marker').popover('hide');
    });
1
Luca Vizzi

Celui-ci fonctionne comme un charme et je l'utilise.

Il ouvrira le popover lorsque vous cliquerez et si vous cliquez à nouveau, il se fermera. Même si vous cliquez en dehors du popover, le popover sera fermé.

Cela fonctionne également avec plus de 1 popover.

    function hideAllPopovers(){
    $('[data-toggle="popover"]').each(function() {
        if ($(this).data("showing") == "true"){
            $(this).data("showing", "false");
            $(this).popover('hide');                
        }
    });
}
$('[data-toggle="popover"]').each(function() {
        $(this).popover({
            html: true,
            trigger: 'manual'
        }).click(function(e) {
            if ($(this).data("showing") !=  "true"){
                hideAllPopovers();
                $(this).data("showing", "true");
                $(this).popover('show');
            }else{
                hideAllPopovers();
            }
            e.stopPropagation();
        });
});

$(document).click(function(e) {
    hideAllPopovers();
});
1
Patrick Nogueira

Voici la solution qui a très bien fonctionné pour moi, si cela peut aider:

/**
* Add the equals method to the jquery objects
*/
$.fn.equals = function(compareTo) {
  if (!compareTo || this.length !== compareTo.length) {
    return false;
  }
  for (var i = 0; i < this.length; ++i) {
    if (this[i] !== compareTo[i]) {
      return false;
    }
  }
  return true;
};

/**
 * Activate popover message for all concerned fields
 */
var popoverOpened = null;
$(function() { 
    $('span.btn').popover();
    $('span.btn').unbind("click");
    $('span.btn').bind("click", function(e) {
        e.stopPropagation();
        if($(this).equals(popoverOpened)) return;
        if(popoverOpened !== null) {
            popoverOpened.popover("hide");            
        }
        $(this).popover('show');
        popoverOpened = $(this);
        e.preventDefault();
    });

    $(document).click(function(e) {
        if(popoverOpened !== null) {
            popoverOpened.popover("hide");   
            popoverOpened = null;
        }        
    });
});
1
Gilles Hemmerlé

J'ai eu un problème pour le faire fonctionner sur bootstrap 2.3.2. Mais je l'ai aimé de cette façon:

$(function () {
  $(document).mouseup(function (e) {
        if(($('.popover').length > 0) && !$(e.target).hasClass('popInfo')) {
            $('.popover').each(function(){
                $(this).prev('.popInfo').popover('hide');
            });
        }
    });

    $('.popInfo').popover({
        trigger: 'click',
        html: true
    });
});
1
oBo

Cette question a également été posée ici et ma réponse fournit non seulement un moyen de comprendre les méthodes de traversée du DOM de jQuery, mais également deux options permettant de gérer la fermeture des popovers en cliquant à l'extérieur.

Ouvrez plusieurs popovers à la fois ou un popover à la fois.

De plus, ces petits extraits de code peuvent gérer la fermeture des boutons contenant des icônes!

https://stackoverflow.com/a/14857326/1060487

1
mattdlockyer

Une autre solution, c’est le problème que j’avais avec le fait de cliquer sur les descendants du popover:

$(document).mouseup(function (e) {
    // The target is not popover or popover descendants
    if (!$(".popover").is(e.target) && 0 === $(".popover").has(e.target).length) {
        $("[data-toggle=popover]").popover('hide');
    }
});
1
Fernando Caraballo

Voici ma solution, pour ce que ça vaut:

// Listen for clicks or touches on the page
$("html").on("click.popover.data-api touchend.popover.data-api", function(e) {

  // Loop through each popover on the page
  $("[data-toggle=popover]").each(function() {

    // Hide this popover if it's visible and if the user clicked outside of it
    if ($(this).next('div.popover:visible').length && $(".popover").has(e.target).length === 0) {
      $(this).popover("hide");
    }

  });
});
1
nates

légèrement modifié la solution @David Wolever:

function closePopoversOnDocumentEvents() {
  var visiblePopovers = [];

  var $body = $("body");

  function hideVisiblePopovers() {
    /* this was giving problems and had a bit of overhead
      $.each(visiblePopovers, function() {
        $(this).popover("hide");
      });
    */
    while (visiblePopovers.length !== 0) {
       $(visiblePopovers.pop()).popover("hide");
    }
  }

  function onBodyClick(event) {
    if (event.isDefaultPrevented())
      return;

    var $target = $(event.target);
    if ($target.data("bs.popover"))
      return;

    if ($target.parents(".popover").length)
      return;

    hideVisiblePopovers();
  }

  function onBodyKeyup(event) {
    if (event.isDefaultPrevented())
      return;

    if (event.keyCode != 27) // esc
      return;

    hideVisiblePopovers();
    event.preventDefault();
  }

  function onPopoverShow(event) {
    if (!visiblePopovers.length) {
      $body.on("click", onBodyClick);
      $body.on("keyup", onBodyKeyup);
    }
    visiblePopovers.Push(event.target);
  }

  function onPopoverHide(event) {
    var target = event.target;
    var index = visiblePopovers.indexOf(target);
    if (index > -1) {
      visiblePopovers.splice(index, 1);
    }
    if (visiblePopovers.length == 0) {
      $body.off("click", onBodyClick);
      $body.off("keyup", onBodyKeyup);
    }
  }

  $body.on("show.bs.popover", onPopoverShow);
  $body.on("hide.bs.popover", onPopoverHide);
}
1
Lee Gary
$('.popForm').popover();

$('.conteneurPopForm').on("click",".fermePopover",function(){
    $(".popForm").trigger("click");
});

Pour être clair, il suffit de déclencher le popover

0
poete

Je le fais comme ci-dessous

$("a[rel=popover]").click(function(event){
    if(event.which == 1)
    {   
        $thisPopOver = $(this);
        $thisPopOver.popover('toggle');
        $thisPopOver.parent("li").click(function(event){
            event.stopPropagation();
            $("html").click(function(){
                $thisPopOver.popover('hide');
            });
        });
    }
});

J'espère que cela t'aides!

0
foxybagga

Une solution encore plus facile, il suffit de parcourir tous les popovers et de cacher sinon this.

$(document).on('click', '.popup-marker', function() {
    $(this).popover('toggle')
})

$(document).bind('click touchstart', function(e) {
    var target = $(e.target)[0];
    $('.popup-marker').each(function () {
        // hide any open popovers except for the one we've clicked
        if (!$(this).is(target)) {
            $(this).popover('hide');
        }
    });
});
0
inostia

Ajoutez la classe btn-popover à votre bouton/lien popover ouvrant la popover. Ce code ferme les fenêtres lorsque vous cliquez en dehors de celui-ci.

$('body').on('click', function(event) {
  if (!$(event.target).closest('.btn-popover, .popover').length) {
    $('.popover').popover('hide');
  }
});
0
Tobias Mühl

@RayOnAir, j'ai le même problème avec les solutions précédentes. Je me rapproche aussi de la solution @RayOnAir. Une chose améliorée est la fermeture popover déjà ouverte lorsque vous cliquez sur un autre marqueur popover. Donc mon code est:

var clicked_popover_marker = null;
var popover_marker = '#pricing i';

$(popover_marker).popover({
  html: true,
  trigger: 'manual'
}).click(function (e) {
  clicked_popover_marker = this;

  $(popover_marker).not(clicked_popover_marker).popover('hide');
  $(clicked_popover_marker).popover('toggle');
});

$(document).click(function (e) {
  if (e.target != clicked_popover_marker) {
    $(popover_marker).popover('hide');
    clicked_popover_marker = null;
  }
});
0
msa7

Si vous essayez d'utiliser Twitter bootstrap popover avec pjax, cela a fonctionné pour moi:

App.Utils.Popover = {

  enableAll: function() {
    $('.pk-popover').popover(
      {
        trigger: 'click',
        html : true,
        container: 'body',
        placement: 'right',
      }
    );
  },

  bindDocumentClickEvent: function(documentObj) {
    $(documentObj).click(function(event) {
      if( !$(event.target).hasClass('pk-popover') ) {
        $('.pk-popover').popover('hide');
      }
    });
  }

};

$(document).on('ready pjax:end', function() {
  App.Utils.Popover.enableAll();
  App.Utils.Popover.bindDocumentClickEvent(this);
});
0
keruilin

J'aime ça, simple mais efficace ..

var openPopup;

$('[data-toggle="popover"]').on('click',function(){
    if(openPopup){
        $(openPopup).popover('hide');

    }
    openPopup=this;
});
0
gstarr

Je suis venu avec ceci:

Mon scénario incluait plus de popovers sur la même page, et les masquer les rendait simplement invisibles. De ce fait, il était impossible de cliquer sur les éléments situés derrière le popover. L'idée est de marquer le lien popover spécifique comme étant "actif" et vous pouvez ensuite simplement "basculer" le popover actif. Cela fermera complètement le popover.

$('.popover-link').popover({ html : true, container: 'body' })

$('.popover-link').popover().on 'shown.bs.popover', ->
  $(this).addClass('toggled')

$('.popover-link').popover().on 'hidden.bs.popover', ->
  $(this).removeClass('toggled')

$("body").on "click", (e) ->
  $openedPopoverLink = $(".popover-link.toggled")
  if $openedPopoverLink.has(e.target).length == 0
    $openedPopoverLink.popover "toggle"
    $openedPopoverLink.removeClass "toggled"
0
Adi Nistor

J'essayais de trouver une solution simple à un problème simple. Les messages ci-dessus sont bons mais très compliqués pour un problème simple. Alors j'ai fait une chose simple. Vient d’ajouter un bouton de fermeture. C'est parfait pour moi.

            $(".popover-link").click(function(){
                $(".mypopover").hide();
                $(this).parent().find(".mypopover").show();
        })
        $('.close').click(function(){
    $(this).parents('.mypopover').css('display','none');
});



          <div class="popover-content">
        <i class="fa fa-times close"></i>
    <h3 class="popover-title">Title here</h3>
your other content here
        </div>


   .popover-content {
    position:relative;
    }
    .close {
        position:absolute;
        color:#CCC;
        right:5px;
        top:5px;
        cursor:pointer;
    }
0
Mohammad Kashif

J'ai trouvé qu'il s'agissait d'une solution modifiée de la suggestion de pbaron ci-dessus, car sa solution activait le popover ("masquer") sur tous les éléments avec la classe "popup-marker". Cependant, lorsque vous utilisez popover () pour le contenu HTML au lieu du contenu de données, comme je le fais ci-dessous, tous les clics dans cette fenêtre HTML active activent le popover ("masquer"), qui ferme rapidement la fenêtre. La méthode ci-dessous parcourt chaque élément .popup-marker et découvre d’abord si le parent est lié à l’identifiant du .popup-marker sur lequel l’utilisateur a cliqué et ne le masque pas. Tous les autres divs sont cachés ...

        $(function(){
            $('html').click(function(e) {
                // this is my departure from pbaron's code above
                // $('.popup-marker').popover('hide');
                $('.popup-marker').each(function() {
                    if ($(e.target).parents().children('.popup-marker').attr('id')!=($(this).attr('id'))) {
                        $(this).popover('hide');
                    }
                });
            });

            $('.popup-marker').popover({
                html: true,
                // this is where I'm setting the html for content from a nearby hidden div with id="html-"+clicked_div_id
                content: function() { return $('#html-'+$(this).attr('id')).html(); },
                trigger: 'manual'
            }).click(function(e) {
                $(this).popover('toggle');
                e.stopPropagation();
            });
        });
0
David