notANerdDev
notANerdDev

Reputation: 1286

What is wrong with this code, trying a click menu?

So there is a left side vertical menu. And it has a small options button. When clicked, it should open a div which will have various filter options. Now, i need it to appear when clicked, and disappear when either the options button is clicked again, or the user clicks anywhere outside the div.

So i have the following code.

//options filter menu animation
$('#optionsmenu').hide(); //hides the menu

$('.optionsfilters').click(function(e){
    var $this = $('#optionsmenu');
    $this.show();
    var left = $('#sidebar').css('width'),
        top = $(this).offset().top;
    $this.css('top', top);
    $this.css('left', left);
});
$(':not(.optionsfilters)').click(function(e){
    $('#optionsmenu').hide();
});

The HTML is

<div id="sidebartitle">
    <h2>Organisation</h2>
    <a id="optionsfilters" class="optionsfilters">Options</a>
</div>
<div id="optionsmenu" class="optionsfilters">
    <h3>Add New</h3>
    <ul>
        <label>Year</label>
        <select>
            <option>2000</option>
            <option>2001</option>
            <option>2002</option>
            <option>2003</option>
            <option>2004</option>
            <option>2005</option>
        </select>
    </ul>
</div>

Its not working together, i.e. the two javascript functions, the first one works alone, when the second one is commented out. The second one works, if i comment out the hide part, and add an alert message. But together, they don't work.

Whats the conflict?

Upvotes: 2

Views: 123

Answers (6)

lee_gladding
lee_gladding

Reputation: 1100

Ok there are a few things you will want to do:

1) If you want the original button to close the menu you will want to use .toggle() rather than .show()

2) You will want to detect a click on the document, to hide the options menu. Which will not be called if it is the options menu that is clicked due to the e.stopPropagation(); (point 4 below).

    $(document).click(function() {
        $('#optionsmenu').hide();
    });

3) You also want to check (as both have the same class) that the .optionsfilters that was clicked was not the filters themselves (otherwise this will stop you clicking an option).

    if( e.target !== this ) {
       return;
    }

4) Use e.stopPropagation(); to stop the event bubbling up to the parents (or document).

This should be what you are looking for:

$('#optionsmenu').hide(); //hides the menu

$('.optionsfilters').click(function(e){
    e.stopPropagation();
    if( e.target !== this ) {
       return;
    }
    var $this = $('#optionsmenu');

    $this.toggle();
    var left = $('#sidebar').css('width'),
       top = $(this).offset().top;
    $this.css('top', top);
    $this.css('left', left);
});

$(document).click(function() {
  $('#optionsmenu').hide();
});

Here is the working fiddle: http://jsfiddle.net/lee_gladding/pny4vq26/

Upvotes: 2

Spokey
Spokey

Reputation: 10994

The problem is mainly that both your click handlers run and one shows the menu then the other one hides it.

Why? Because that's not a good selector. Until you reach the element with the class .optionsfilters your click goes through body, #sidebartitle and then it reaches the element a. And since the parents of that element don't have the class .optionsfilters, it will hide the menu.

So I changed the code a little. First of all don't use the class as a click handler. Use the IDs

$('#optionsfilters').click(function (e) { // ID of options
    var $this = $('#optionsmenu');
    $this.toggle(); // toggle show/hide when click on it
    var left = $('#sidebar').css('width'),
        top = $(this).offset().top;
    $this.css('top', top);
    $this.css('left', left);
});

The you need to check when you click outside of the #optionsmenu. For that you attach a click handler on document and check the e.target

$(document).click(function (e) {
    if (!$(e.target).closest('.optionsfilters').length)
         $('#optionsmenu').hide();
});

If the e.target itself is not .optionsfilters and is not a child of a parent with that class then you hide the menu.

Working example below.

//options filter menu animation
$('#optionsmenu').hide(); //hides the menu

$('#optionsfilters').click(function (e) {
    var $this = $('#optionsmenu');
    $this.toggle();
    var left = $('#sidebar').css('width'),
        top = $(this).offset().top;
    $this.css('top', top);
    $this.css('left', left);
});
$(document).click(function (e) {
    if (!$(e.target).closest('.optionsfilters').length) $('#optionsmenu').hide();
});
#optionsmenu{
  background:lightblue;  
}
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<div id="sidebartitle">
     <h2>Organisation</h2>
 <a id="optionsfilters" class="optionsfilters">Options</a>

</div>
<div id="optionsmenu" class="optionsfilters">
     <h3>Add New</h3>

    <ul>
        <label>Year</label>
        <select>
            <option>2000</option>
            <option>2001</option>
            <option>2002</option>
            <option>2003</option>
            <option>2004</option>
            <option>2005</option>
        </select>
    </ul>
</div>

Upvotes: 1

ste.cape
ste.cape

Reputation: 45

I can't understand, but if your menu is hide (first instruction in your js), how can you click on it for show it?

EDIT: Didn't noticed the class attriute on previous div. My fault.

Upvotes: -1

ymz
ymz

Reputation: 6914

a classic demonstration of stopPropagation in jQuery (http://api.jquery.com/event.stoppropagation/)

here is a fiddle that simplify this: http://jsfiddle.net/ymzrocks/98082xk7/1/

in short:

$('.optionsfilters').click(function(e)
{
    e.stopPropagation(); // or elese it will fire the parent event
    var $this = $('#optionsmenu');
    $this.show();
    var left = $('#sidebar').css('width'),
        top = $(this).offset().top;
    $this.css('top', top);
    $this.css('left', left);
});

Upvotes: 1

wbars
wbars

Reputation: 533

Here you go

//options filter menu animation
var filters = $('.optionsfilters');
var options = $('#optionsmenu');

options.hide(); //hides the menu

filters.click(function(e){
    $('#optionsmenu').toggle();
    var left = $('#sidebar').css('width'),
        top = $(this).offset().top;
    _this.css('top', top);
    _this.css('left', left);
});

$(document).click(function(e) {
    if (!filters.is(e.target) && filters.has(e.target).length === 0) {
        options.hide();
    }
});

http://jsfiddle.net/6e86hdwc/

Upvotes: 1

Sacha
Sacha

Reputation: 569

Be careful using :not for any other elements, because if - for example - you click the dropdown, the sidebar hides again, which I doubt you want to happen.

It'd be better to define areas you want to make the sidebar hide when clicked, like the main content area.

Also since you use the class .optionsfilters on your sidebar menu, when you click in there the sidebar will hide again.

Try this JS Fiddle.

Upvotes: 0

Related Questions