2

One of the things I'm working on for an upcoming project is a callout/tooltip system. In theory, it's a simple little thing. You take some markup like this:

<script src='jQuery 1.10.1'></script>
<a href='#' id='callout-link'>Click Me</a>

<div id='callout-id' class='callout hidden'>
    This is my tooltip text.
</div>

/* CSS Styling... */
.callout {
     position: absolute;
     z-index: 2;
}

.hidden {
     display: none;
}

...and bind it up in JavaScript so that whenever you click some link it toggles visibility.

// ...Page Script...
var $link = $('#callout-link'),
    $callout = $('#callout-id');

$(document).ready(function () {
    var callout = new MyCallout($link, $callout);
    callout.bindToControlClick();
});

// ...Object Definition...
var MyCallout = function ($control, $callout) {
    init();

    // Public Scope...
    var pub = {};
    pub.$actionControl = $control;
    pub.$callout = $callout;

    pub.bindToControlClick = function () {
        pub.$actionControl.click(function (e) {
            e.preventDefault();
            e.stopPropagation();

            toggleCallout();                
        });
    }

    // Private Scope...
    function init() {
        // The callout-content subdiv is necessary for other operations that are
        // likely not relevant to the problem I am encountering.
        if($callout.children('.callout-content').length == 0)
            $callout.wrapInner('<div class="callout-content" />');
    }

    function toggleCallout() {
        if(pub.$callout.is(':hidden')) {
            // Show is actually more complicated than this, but that's not the problem.
            pub.$callout.show();
        } else {
            pub.$callout.hide();
        }
    }

    return pub;
};

The problem is that, while the callout is initially hidden from view, and appears on click, attempting to toggle the callout off fails. When I add a breakpoint and watch value in the Chrome debugger for $callout.is(':hidden'), even if the element is visible the watch value evaluates to true.

Based on jQuery's entry for the :hidden selector, it appears that a number of properties are being checked at any given time, which brings me to the Question: what is it in my setup that's managing to fool jQuery's :hidden selector into always believing the callout to be hidden, even when such is no longer the case?

EDIT: Since I notice a recent question of mine in the side panel, my team has since ensured that our app is running in IE9 Standards mode, so environment should not be a problem this go-round.

EDIT 2: Fiddle of the problematic code (UPDATED! Problem reproduced in full.)

Andrew Gray
  • 3,756
  • 3
  • 39
  • 75
  • A jsFiddle would be welcome – Brewal Aug 07 '13 at 16:26
  • 1
    Are you testing this in browsers other than IE9? The IE debugger is OK when you're trying to find IE-only issues, but it's not nearly as usable as Firebug or the Chrome debugger. – Pointy Aug 07 '13 at 16:29
  • @Pointy: I'm using the Chrome debugger for JavaScript debugging, specifically. – Andrew Gray Aug 07 '13 at 16:30
  • @Brewal: What is a jsFiddle, exactly? I haven't heard of this before. – Andrew Gray Aug 07 '13 at 16:32
  • You realize that `.show()` and `.hide()` won't affect that "hidden" class, right? I wonder if that has something to do with it. (I'm not sure how/why that'd affect things ...) – Pointy Aug 07 '13 at 16:33
  • 2
    [jsFiddle](http://jsfiddle.net/) is a great thing to see the behaviour of your js/css/html code – Brewal Aug 07 '13 at 16:33
  • @Pointy - The `hidden` class has no effect. I took the `hidden` class off of the callout, and this issue is still occurring for some reason. I was hoping that was it...I guess it isn't... – Andrew Gray Aug 07 '13 at 18:08

2 Answers2

2

Try using hasClass instead :

function toggleCallout() {
    if(pub.$callout.hasClass('hidden')) {
        pub.$callout.removeClass('hidden');
    } else {
        pub.$callout.addClass('hidden');
    }
}

Or even quicker with toggleClass :

function toggleCallout() {
    pub.$callout.toggleClass('hidden');
}

Also make sure to put your MyCallout declaration before using it, otherwise you will get an error like

Uncaught TypeError: undefined is not a function

when calling

var callout = new MyCallout($link, $callout);

The working Fiddle

Brewal
  • 8,067
  • 2
  • 24
  • 37
  • While a good suggestion, there are other things at work here - I wasn't *exactly* asking for an alternate implementation, I'm trying to figure out why `:hidden` is being fooled; when I have that critical piece of the puzzle, I can find the fix that's most suitable to the particulars of my team's project on my own. – Andrew Gray Aug 07 '13 at 19:55
  • 1
    Well, I didn't really looking for what caused the problem here because I think you shouldn't use the jquery hidden selector here. It's cleaner to just toggle the class instead of keeping the hidden class even if the element is actually displayed. I will edit my answer with `toggleClass()` jQuery method, that is even more fitted. – Brewal Aug 08 '13 at 12:42
0

The problem was actually a CSS problem - while my code is sane (as the jsFiddle in my OP shows), the .callout div was rendering with default dimensions of width: 0; height: 0, which causes :hidden to select the element.

All I had to do was add the following CSS to solve my problem:

.callout {
    width: 1em;
    height: 1em;
    /* ...As before... */
}

Lesson: Always inspect elements! What looks like a script error, may in fact be something else entirely.

Andrew Gray
  • 3,756
  • 3
  • 39
  • 75