views:

54

answers:

4

I am trying to basically disable the click event on a <div> temporarily.

I have tried the following (preview):

$('hello').observe('click', function (e) {
    e.stop();
});

$('hello').observe('click', function (e) {
    alert('click not stopped!');
});

However, when #hello is clicked, the alert box still appears. I do not want the second attached handler to be called, and I do not want to change the second handler.

I will also accept a solution such as:

$('hello').observe('click', function (e) {
    alert('click not stopped!');
});

$('hello').disableEvent('click');

// Now, handler won't be called

$('hello').observe('click', function (e) {
    alert('click not stopped (2)!');
});

// New handler won't be called, either

$('hello').enableEvent('click');

// Now, handler will be called

I am using the Prototype.js framework. This doesn't seem to be a browser-specific issue.

A: 

Each time you assign the observation of 'click', you're overwriting the previous one. It's just the way it is, and you will have to find another solution.

You could use a javascript variable, which holds the value of whether or not that item is disabled, and the click handler can check for that variable. It could be a hidden input box too.

Fosco
You're wrong; the event handler isn't overridden. `e.stop()` *is* called, but it doesn't seem to do what I want.
strager
You're right, it's not overwritten, but that doesn't change much in this scenario. My suggestion about using a separate variable is still valid however, as seconded by roosteronacid below.
Fosco
A: 

Can't you just set the object's disabled property to true?

C Bauer
That doesn't seem to work (`<div disabled="disabled">`), sadly.
strager
it would be disabled="true", and it's html standard... see http://stackoverflow.com/questions/1957067/div-disabled-or-greyed-out Edit: check the accepted answer, not the upvoted.
C Bauer
Nope; doesn't work either.
strager
+1  A: 

When you assign handlers to events; you are basically just storing a set of functions to be executed when an event fires.

When an event fires, the handlers you've added are executed in the order they we're added. So if you we're to add three handlers to a div's click-event:

$("div").observe("click", function ()
{
    alert("one");
});

$("div").observe("click", function ()
{
    alert("two");
});

$("div").observe("click", function ()
{
    alert("three");
});

.. you would get three alerts ("one", "two" and "three") when the click event of the div element fires. Those three alerts will still get shown, if you put in:

$("div").observe("click", function (e)
{
    e.stop();
})

.. because you are only canceling the event for one particular handler. Not all associated handlers.


So what you will need to do is use a reference variable, which keeps track of wether the click event is allowed to fire:

var cancelClickEvent = true;

$("div").observe("click", function ()
{
    // if cancelClickEvent is true, return the function early, to
    // stop the code underneath from getting executed
    if (cancelClickEvent) return;

    // your code goes here
});

You will then need to implement the above if-clause in all your handlers.

roosteronacid
Okay, now I understand a little better. However, I really don't want to modify other handlers. I'm considering overriding `Element.observe` to wrap the handler with a check for some flag on `Event`. Would this work, maybe?
strager
It's never a good idea to override core functionality--you never know what you might break.. And what are you gonna do when a new version comes around? Re-implement your modifications?.. What if the API changes?.. If you want a cleaner approach; use the disabled property on your div elements, instead of a global reference variable to determine if the event should be cancelled.
roosteronacid
I wasn't going to use a global; I was going to use a flag on the event instance (which is passed among handlers). I agree that overriding the core isn't a good idea, but I see little other options at the moment.
strager
If you can somehow modify the Event objects prototype, without possibly breaking any core features; you have my blessings ;) ... Good luck mate.
roosteronacid
A: 

As I said in comments to roosteronacid's answer, I wrote an extension to Event.observe. Works in most browsers, but not IE.

// XXX HACK XXX
(function () {
    var handlerCache = $A([ ]);

    function findHandler(either) {
        var pair = handlerCache.find(function (pair) {
            return $A(pair).member(either);
        });

        return pair && pair[0];
    }

    function addHandler(handler) {
        function newHandler(e) {
            if (!e.halted) {
                handler.apply(this, arguments);
            }
        }

        handlerCache.push([ handler, newHandler ]);

        return newHandler;
    }

    Event.observe = Event.observe.extended(function ($super, element, eventName, handler) {
        handler = findHandler(handler) || addHandler(handler);

        $super(element, eventName, handler);
    });

    Event.stopObserving = Event.stopObserving.extended(function ($super, element, eventName, handler) {
        handler = findHandler(handler) || handler;

        $super(element, eventName, handler);
    });

    Element.addMethods({
        observe: Event.observe
    });

    Event.prototype.halt = function () {
        this.halted = true;
    };
}());

Note: Function.prototype.extended is a custom function which puts the original Event.observe in as $super.

strager