Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "click outside" option to hide tooltip #300

Closed
yairEO opened this issue Jun 6, 2017 · 36 comments
Closed

Add "click outside" option to hide tooltip #300

yairEO opened this issue Jun 6, 2017 · 36 comments
Labels
feature This would be nice to have. PRIORITY: low

Comments

@yairEO
Copy link

yairEO commented Jun 6, 2017

Suggestion:

I've made a plugin for Tether (back in the day) for tooltips and I pass an option (Boolean)
for tooltips (only ones which are opening on "click/tap/manual" event) and it listens
to clicking outside of the tooltip area and then closes them.

I guess it will look like this:

var tooltip = new Tooltip(referenceElement, {
    title: "Hey there",
    trigger: "click",
    options: {
       clickOutside : true
   }
});

Here's a snippet of my code which will give some idea to what I did (modified for simplicity purposes):

if( checkClickOutside ){
    timer = setTimeout(function(){
        // bind a "click outside" event on the document itself
        // (this event MUST be removed when the tooltip is
        // removed to prevent it from being created again and
        // not to pollute the document with events)
        $(document.body).off('click.tooltip_outside')
                        .on('click.tooltip_outside', onClickOutside.bind(that));
    }, 50);
}

function onClickOutside(e){
    if( !$(e.target).closest('.ttip').length ){
        $(document).trigger('clickOutside', [e.target]);
        $(document.body).off('click.tooltip_outside');
        this.hide();
    }
}

I've been using Tether for YEARS and only today I've found out about your lib. great work!
I was always sad how Tether got so shitty and abandoned... this seems so much better in terms of code.
Tether's source was so horrible..OMG.. super bad.


BTW - would be nice if the tooltip example page would show an example where some options are passed

@FezVrasta
Copy link
Member

FezVrasta commented Jun 6, 2017

Tooltip.js mimics the Bootstrap Tooltip API.

Do you know if the BS tooltip supports this feature? I'd like to copy its API for this as well if they do support it.

@FezVrasta FezVrasta added feature This would be nice to have. DIFFICULTY: low PRIORITY: low labels Jun 6, 2017
@FezVrasta FezVrasta changed the title tooltip.js -> add "click outside" option which hides it Add "click outside" option to hide tooltip Jun 6, 2017
@yairEO
Copy link
Author

yairEO commented Jun 6, 2017

I don't use bootstrap.. so I don't know

Even if they don't have this option, I think this is a must-have feature because it's very common UX request to have a tooltip disappear when clicking outside and if it's not included in bootstrap, it shouldn't stop you from adding a few extra stuff, if they are important enough, no?

wouldn't you say this is an important feature?

@FezVrasta
Copy link
Member

Yes I'm just saying that if BS has it, I want to mimic its API, if they don't have it, I'll add it with custom API

@yairEO
Copy link
Author

yairEO commented Jun 6, 2017

btw, how do I destroy all the tooltips instances on the page? clean up everything.

This is highly useful when you have a tooltip which opens on click, and that tooltip is "attached" to some element which is in a modal window, and now the user closed that modal window, or perhaps it was a carousel and the user changed slide. this would mean I would need to remove all the tooltips from the screen (but do a real cleanup and not simply remove items by class from the DOM)

@FezVrasta
Copy link
Member

.dipose()

@yairEO
Copy link
Author

yairEO commented Jun 6, 2017

it's not showing in any of the documentation pages :) just saying

@FezVrasta
Copy link
Member

Right...

@remisture

This comment has been minimized.

@i8beef

This comment has been minimized.

@moyarich
Copy link

This is how it is done for bootstrap popovers:
http://jsfiddle.net/moyarich/adzsp0L6/

$(function () {
    var element = '.example';
    $(element).popover({
    });

    $('body').on('click', function (e) {
				
        //Use each to hide Popovers with the same class
         $(element).each(function(index, elm) {
    				hidePopover(elm, e);
        }); 
    });

     // hide any open popovers when anywhere else in the body is clicked
    var hidePopover = function(element, e){
      if (!$(element).is(e.target) && $(element).has(e.target).length === 0 && $('.popover').has(e.target).length === 0){
        $(element).popover('hide');
      }
    }
});

@FezVrasta
Copy link
Member

@moyarich is it configurable?

@Kikketer
Copy link

Kikketer commented Sep 18, 2017

I assume the bootstrap example above wouldn't work if an event is stopPropagation()ed? Say you click on the next H1 tag and that has a click event that stops propagation. It would never then close the dialog.

Just evaluating my use case.

[Update]: I've created my own popover "click anywhere to close" where it places a fixed div under the popup. Any click on that div closes the popover and stops propagation. It works well for my use case because I don't have any control of most of the application. The body-click method may work well (and feels easier, cleaner, etc) when you have full control of your application.

@FezVrasta
Copy link
Member

I think this is the part of the Bootstrap documentation where it talks about this feature.

https://getbootstrap.com/docs/4.0/components/popovers/#dismiss-on-next-click

I don't think I understand how it is supposed to work.

@moyarich
Copy link

moyarich commented Nov 2, 2017

@FezVrasta bootstrap uses focusin to show the popper and and focusout to hide it.
when you click the button it creates a tooltip, when the button loses focus the tooltip disappears. it doesn't work very well because when you try to click inside the tooltip, it disappears

@frzsombor
Copy link

@moyarich Thanks man, this function is awesome!
I've started using it in my projects with tooltip.js

However just a few notes about that code:

I've replaced
$('body').on('click', function (e) {
with:
$('body').on('click touchend', function (e) {
for better mobile user experience, and

about the long if, when you broke it to three lines:

!$(element).is(e.target) 
&& $(element).has(e.target).length === 0 
&& $('.your-custom-tooltip').has(e.target).length === 0 // <-- "tooltip interaction"

That "tooltip interaction" line is also really interesting and useful.
If you don't want the user to interact with your tooltip (so close it if user clicks is), you can comment it out, but when you want the user to be able to click links/buttons in it, you leave it uncommented. So if @FezVrasta you want to implement this code once, it would be great to have this optional with a tooltip.js option.

@steven-prybylynskyi
Copy link

!$(element).is(e.target) 
&& $(element).has(e.target).length === 0 

can be simplified to

!$(e.target).closest(element).length;

@agurtovoy
Copy link

agurtovoy commented Feb 14, 2018

bootstrap uses focusin to show the popper and and focusout to hide it. when you click the button it creates a tooltip, when the button loses focus the tooltip disappears. it doesn't work very well because when you try to click inside the tooltip, it disappears

This can be fixed, though, by adding tabIndex="-1" attr to the tooltip/popover itself, thus making it focusable, and subscribing to focusout event on both the button and the tooltip/popover and doing something along the lines of:

    onFocusOut = event => {
        const newTarget = event.relatedTarget;
        if ( !this.button.contains( newTarget ) && 
             !this.tooltip.contains( newTarget ) ) {
            // dismiss the tooltip/popover here
        }
    }

@dangelion
Copy link

Any news about this implementation? If not, someone can provide a clear and working js script to use? thanks

@mikila85
Copy link

mikila85 commented Jul 23, 2018

@FezVrasta

more than a year has passed and a simple click event can't be added.. amazing guys!!


just noticed priority: low... LOL... such an important usage for most users.
click button to open tooltip, click outside to close...

@steven-prybylynskyi
Copy link

abandoned?

@FezVrasta
Copy link
Member

I mean, any of you could read the issue, understand the requirements for such feature to exists, and send a PR to implement it. The issue is open rather than closed for this very reason.

@piglovesyou
Copy link

piglovesyou commented Jul 28, 2018

So

  • Twitter Bootstrap doesn't support the behavior (needs to setup a listener manually) (I checked it)
  • Still everyone wants this in a configurable way

, yes? Then how about this:

new Tooltip(document.querySelector('#anchor'), {
    trigger: 'click',
    closeOnDocumentClick: true,
});

Not a quality gif ;(

In my opinion, I'm not really sure if tooltip.js should have this, because it's very easy to write and might require further options (keep showing two tooltips at a same moment, for example). What do you think? @FezVrasta

@FezVrasta
Copy link
Member

I may think of a way to define a "group" name to a set of tooltips, so that if you click on any of them, the other don't get closed. But if you click on any other tooltip with a different group or without group, the tooltips close.

I think that would give enough flexibility.

@piglovesyou
Copy link

Might be true. The spec will go something like:

new Tooltip(ref, {
    trigger: 'click',
    closeOnDocClick: true,  // false by default
    closeOnDocClickExcept: 'group1',  // 'default' by default
});

Suggestion: How about starting the closeOnDocClick option without the "group" idea? Not too late after someone actually wants to have it.

@FezVrasta
Copy link
Member

Yes sure, I'm not quite sold on the closeOnDocClick actually, I think it would be easier to understand if we called it closeOnClickOutside

@piglovesyou
Copy link

Sure :) Let me give it a try.

@mikila85
Copy link

are we merging?

@psixdev
Copy link

psixdev commented Oct 15, 2018

Is this functionality possible to add to popper and react-popper?

@piglovesyou
Copy link

@psixdev Yes, but only if someone develop it. I don't think it's a good idea for popper.js to have it, since it's a core and better not to have such function that each users expect different depending on their needs.

Besides, there is a lot of tickets saying "popper.js is heavy". I don't think it's true, but some deliberation not to have a new option would be needed I think.

@mindtechmedia
Copy link

This doesn't work with a touch event outside the element, correct?

@elie222
Copy link

elie222 commented Jan 23, 2019

I recommend using this library for this functionality:
https://www.npmjs.com/package/react-popper-tooltip

@elie222
Copy link

elie222 commented Jan 24, 2019

For those using React, I created a gist of an HOC that you can attach to any component to close it when clicked outside:

https://gist.github.com/elie222/850bc4adede99650508aba2090cd5da1

@toovy
Copy link

toovy commented Mar 22, 2019

I can confirm that closeOnClickOutside does not work with touches on iOS/iPhone. The name of the parameter explicitly suggests, that it handles clicks only, but I think touches should also be handled.

It seems that here only a click listener ist registered: https://github.com/FezVrasta/popper.js/blob/b4aae4b6cffffd3f61458c384b999a86c27b16a7/packages/tooltip/src/index.js#L361

@atomiks
Copy link
Collaborator

atomiks commented Mar 22, 2019

@toovy the problem is iOS doesn't fire click events unless it was initiated on a "clickable" element (one that can receive focus)

A fix is to use document.body.style.cursor = 'pointer' if detecting iOS. touchend doesn't have the same behavior as a regular click and I wouldn't recommend it, because unintentional touches outside the popper can accidentally close it. It should be an intentional tap which is handled by the "click" event.

A side effect of using the pointer cursor on iOS is can create is tap highlights if the height of the document is smaller than the screen (I think?), but it can fixed by using -webkit-tap-highlight-color: transparent; on the body.

All problems will go away after this - it makes it behave like Android then (which I think is the correct behavior anyway -- iOS's quirks are incredibly annoying).

@toovy
Copy link

toovy commented Mar 22, 2019

@atomiks thanks for the tips, I'll try that out.

@coskuntekin
Copy link

Stimulus.js solution if anybody interesting here is;

import { Controller } from "stimulus";
import { createPopper } from '@popperjs/core';

export default class extends Controller {

  static targets = ['tooltip'];

  open(event) {
    this.tooltipTarget.hidden = false;
    createPopper(event.currentTarget, this.tooltipTarget, {
      placement: 'top',
      modifiers: [
        {
          name: 'offset',
          options: {
            offset: [0, 15],
          },
        },
      ]
    });
    this.hide(event);
  }

  hide() {
    const clicker = (event) => {
      if (!this.tooltipTarget.contains(event.target)) {
        this.tooltipTarget.hidden = true;
      }
    };
    document.addEventListener('touchstart', clicker, false);
  }

}
%section.mb-3{'aria-label' => 'Bar chart', 'data-controller' => 'chart'}
  %a.tooltip{'aria-label' => 'Tooltip', 'data-target' => 'chart.tooltip', hidden: '', href: '#'}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature This would be nice to have. PRIORITY: low
Projects
None yet
Development

No branches or pull requests