skip to Main Content

In the snippet below, I have a Bootstrap Popover which supports both Hover & Click modes. On Click, the window stays open and should close either on (1) self-click (link again), or (2) any outside click.

PROBLEM: After an outside click, the popover goes to the “currently open” mode. This means that if you hover over it again, the window is stuck and won’t disappear. What should happen is, after an outside click, you revert to the original Hover-and-Disappear mode when you hover over it again. The same behavior is expected here as when hovering after an inside-close-click, which is the original state. Am I forgetting something?

$('#linkPopover').popover({
  trigger: 'hover click', 
  content: 'This is my content', 
  title: 'TITLE'
});

// -----------------
// Just with the above (and no other code), the Hover and Click-Toggle 
// works within the SAME Popover window,
// but now I need to also remove the visible Popover on ANY CLICK OUTSIDE.

// However, although the below works, *after* clicking outside the Popover is in the CLICKED 
// mode i.e. it doesn't hover anymore

$('body').on('click', function(e) {
	    $('[data-toggle="popover"]').each(function () {
	        if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.popover').has(e.target).length === 0) {
              // From outside click with the popover open, need to hide
	            $(this).popover('hide');
	        }	    	
	    });  
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>

<a id="linkPopover" data-toggle="popover">POPOVER</a>

<br/><br/><br/><br/><br/><br/><br/>
<br/><br/><br/><br/><br/><br/><br/>
<br/><br/><br/><br/><br/><br/><br/>
End of body

3

Answers


  1. Chosen as BEST ANSWER

    I actually solved the infinite-loop in eminememinem's original answer (due to trigger('click')) by introducing a custom flag parameter in the body.click. You can do that as

         $('body').on('click', function (e, manualClickTriggered) {
    

    We set the flag in the special case of our own re-triggering. If so we ignore the regular logic.

    Full snippet:

    $('[id*="linkPopover"]').popover({
      trigger: 'hover click', 
      content: 'This is my content', 
      title: 'TITLE'
    });
    
    // Close any visible popover if clicked outside 
    // The "manualClickTriggered" flag indicates that we initiated the special Click event ourselves in order to turn off the popover, in which case skip the regular logic.
    $('body').on('click', function (e, manualClickTriggered) {
      var linkId = null;
      if (e.target.id.indexOf("linkPopover") != -1) {
        linkId = e.target.id;
      }
    		
      if (manualClickTriggered != true) {
        if (e.target.id.indexOf("linkPopover") == -1) {  // Not coming from any Popover Link (outside click) 
          $('a[id^="linkPopover"][data-toggle="popover"]').each(function () {	    	
            if( !$(this).is(e.target) && ($(this).next('div.popover:visible').length )) {
              $(this).trigger('click', true);
            }
          });
        }
        else // Coming from some Popover link
        {
            $('a[id^="linkPopover"][data-toggle="popover"]').each(function () {
              if ($(this).attr('id') != linkId && $(this).next('div.popover:visible').length) {
                $(this).trigger('click', true);
              }
            });
          }
        }
    });
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
    
    <a id="linkPopover1" data-toggle="popover">POPOVER 1</a><br/>
    <a id="linkPopover2" data-toggle="popover">POPOVER 2</a><br/>
    <a id="linkPopover3" data-toggle="popover">POPOVER 3</a><br/>
    <a id="linkPopover4" data-toggle="popover">POPOVER 4</a><br/>
    
    <br/><br/><br/><br/><br/><br/><br/>
    <br/><br/><br/><br/><br/><br/><br/>
    <br/><br/><br/><br/><br/><br/><br/>
    End of body


  2. This code might help you. I changed your conditional statements to check if the user is clicking outside of the popover trigger && if the popover is visible, then trigger the click event of the popover trigger.

    I thought of doing this because the click event is the event that occurs when you click the trigger itself.

    $(document).ready(function(){
    
    $('#linkPopover').popover({
      trigger: 'hover click',
      content: 'This is my content', 
      title: 'TITLE'
    });
    
    $('body').on('click', function(e) {
        $('[data-toggle="popover"]').each(function () {
            if(!$(this).is(e.target) && ($("#linkPopover").next('div.popover:visible').length)){
            $("#linkPopover").trigger('click');
        }
        });
    });
    
    });
    <script
      src="https://code.jquery.com/jquery-3.3.1.min.js"
      integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
      crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
    
    <body>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
    
    <a id="linkPopover" data-toggle="popover">POPOVER</a>
    
    <br/><br/><br/><br/><br/><br/><br/>
    <br/><br/><br/><br/><br/><br/><br/>
    <br/><br/><br/><br/><br/><br/><br/>
    End of body
    </body>
    Login or Signup to reply.
  3. My first answer produces the Maximum call stack size exceeded error due to an infinite loop when there are more than 1 popover (see my first answer and comments).

    To solve this: I use .off on the body click handler whenever the system is about to execute the trigger('click'). After that I reinitialize the body click handler. See sample below.

    $(document).ready(function(){
    
    function initializeClick(){
    $('body').on('click', function(e) {
        $('[data-toggle="popover"]').each(function () {
            if(!$(this).is(e.target) && ($(this).next('div.popover:visible').length)){
            $('body').off('click');
            $(this).trigger('click');
            initializeClick();
            }
        });
    });
    }
    
    $('#linkPopover').popover({
      trigger: 'hover click',
      content: 'This is my content', 
      title: 'TITLE'
    });
    
    $('#linkPopover2').popover({
      trigger: 'hover click',
      content: 'This is my content', 
      title: 'TITLE'
    });
    
    $('body').on('click', function(e) {
        $('[data-toggle="popover"]').each(function () {
            if(!$(this).is(e.target) && ($(this).next('div.popover:visible').length)){
            $('body').off('click');
            $(this).trigger('click');
            initializeClick();
            }
        });
    });
    
    });
     <script
          src="https://code.jquery.com/jquery-3.3.1.min.js"
          integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
          crossorigin="anonymous"></script>
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
        <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
    
        <body>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
        <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
    
        <a id="linkPopover" class="btn btn-light" data-toggle="popover">POPOVER</a>
    <a id="linkPopover2" class="btn btn-light" data-toggle="popover">POPOVER2</a>
    
        <br/><br/><br/><br/><br/><br/><br/>
        <br/><br/><br/><br/><br/><br/><br/>
        <br/><br/><br/><br/><br/><br/><br/>
        End of body
        </body>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search