tags:

views:

79

answers:

7

I have a collection of span elements. In the code I have a global variable that represents the "selected" object. Using the click event when a span is clicked I reset the object's class, reset the global variable, then set the object to the variable and change its class (to make it "highlighted"). This effectively toggles selection when clicking an object.

var currentItem = null;
$( ".item" ).click( function() {
    if( $( this ).hasClass( "selected" ) ) {
        $( this ).removeClass( "selected" )
        currentItem = null;
    } else {
        if( $( ".item" ).hasClass( "selected" ) ){
            $( ".item" ).removeClass( "selected" )
        }

            $( this ).addClass( "selected" );
            currentItem = $( this );
        }
} );

What I'd like to be able to do is unselect when clicking on an empty area of the page. I tried creating a click event on the body object, but that overrode the span click event so nothing was selected. I'm a complete jQuery noob and not sure where to go with this.

+7  A: 

Use event.stopPropagation() in your current handler so that click doesn't bubble up to the <body>, triggering it's handler as well, then your approach works, like this:

var currentItem = null;
$(".item").click(function(e) {
  if($(this).hasClass("selected")) {
    $(this).removeClass("selected")
    currentItem = null;
  } else {
    $(".item.selected").removeClass("selected");
    currentItem = $(this).addClass("selected");
  }
  e.stopPropagation();
});
$(document).click(function() {
  $(".item.selected").removeClass("selected");
});

You can view a demo here, one suggestion though, if only one element can be selected do you need to keep track of it? If lookup cost isn't a factor, you could aways find the currentItem by doing $(".item.selected"), simplifying this code quite a bit. I'm not sure how you're using currentItem, just an option you have :)

Nick Craver
Nick... do you ever sleep? :D
Gert G
@Gert G - Every third sunday on odd months, yes :)
Nick Craver
LOL - That's what I thought. Btw... Good answer. :)
Gert G
+5  A: 

You did well setting a click event on the body. BUt you must also tell the span click event to stop the propagation of the event..

$( ".item" ).click( function(event) {

    event.stopPropagation();
    //your code
});
Gaby
+2  A: 

Your idea to place the click event on the body is fine.

You just need to be aware that clicking on any descendants of body will cause the click event to bubble up to body, and fire that handler.

You need to stop that propagation in your span event handler.

$( ".item" ).click( function(event) {
    event.stopPropagation();
    ...

jQuery Docs

event.stopPropagation() http://api.jquery.com/event.stopPropagation/


EDIT:

Another valid alternative would be to simply return false at the end of the handler.

$( ".item" ).click( function() {
    // Your code...
    return false;
});
patrick dw
A: 
lewiguez
A: 

In addition to the answers stated (unless I missed this), you needn't attach the click() event to the body when the page loads -- you can simply do it after the item is selected. Not that it hurts since you're stopping propagation, but in my view its more "correct" to only activate/set an event handler for the duration in which it is relevant.

Brian Lacy
+1  A: 

You don't necessarily need a global variable since your DOM holds that state info, too.

// de-select old selection and select new item
$(".item").click( function(evt) {
  $(".item.selected").removeClass("selected");
  $(this).addClass("selected");
  evt.stopPropagation();
});

// de-select everything
$("body").click( function() {
  $(".item.selected").removeClass("selected");
});

// your currently selected element can be retrieved by this anytime:
$(".item.selected")[0]

Not tracking state twice (in the DOM and in a global variable) spares you two headaches:

  • carefully updating the global variable all the time
  • bugs that arise of "global" state getting out of sync with "DOM" state
Tomalak
A: 

Too much code, we can do it easier:


"use strict";
/*global $*/
$(function () {
  $('html').live('click', function (e) {
    $('.item.selected').removeClass('selected');

    var $t = $(e.target);
    if ($t.hasClass('item')) {
      $t.addClass('selected');
    }
  });
});

Using 'html' to bind event is better due body can be not 100% height, for example. And clicking on empty area return not expected result.

We do not need global object, as this script guarantees that we have only one selected span and we can refer to it with:


$('.item.selected'); 

sbmaxx