happy_story
happy_story

Reputation: 1155

How can you cancel a customEvent, if preventDefault() only prevents default browser action?

const log = console.log;

const child = document.getElementById('child');
const parent = document.getElementById('parent');


parent.addEventListener('newColor', function(e) { 
    e.preventDefault()
    log('parent')
    this.style.color = e.detail.textColor;
})

child.addEventListener('newColor', function(e) { 
    log('child')
    this.style.color = e.detail.textColor;
    this.style.backgroundColor = e.detail.bgrColor;
})


function changeColor() { 
    const myEvent = new CustomEvent('newColor', { 
        detail: { 
            textColor: 'red',
            bgrColor: 'blue',
        },
        cancelable: true,
        bubbles: true
    })

    child.dispatchEvent(myEvent);
}


changeColor()
<p id="parent">this is the parent<span id="child"> THIS IS THE CHILD ELEMENT </span></p>

Since event.preventDefault() only prevent the default browser action, how can a custom Event be 'canceled' then?

For example, in here:

$("#but").click(function (ev) {
    ev.preventDefault()
    alert('child button clicked');
  })
 <script src="https://code.jquery.com/jquery-3.6.0.js"></script>


<button id="but">button</button>

the preventDefault() only prevents the default action, which would be to submit a form, not the execution of the rest of the function statements.

So, what is the point of having 'cancellable' property when you can't actually cancel a event with preventDefault()?

Upvotes: 2

Views: 1248

Answers (2)

Y.T.
Y.T.

Reputation: 2739

Usually when you call Event.preventDefault() on a browser-provided object, for example:

document.querySelector("#id-checkbox").addEventListener("click", function(event) {
     console.log("run") // This is still run
     event.preventDefault()
});

It will not trigger browser-defined default action like checking the checkbox (but your code will still run, so the event is not "cancelled"). It is in fact equivalent to: (not equivalent exactly, read here)

function(event) {
    console.log("run") // This is still run
    return false
});

So what Event.preventDefault() do is canceling default browser actions afterward. However, you cannot define default action on a custom Event. Then how is it still useful? Actually it is still useful on some special occasions. To demonstrate how it will work with custom Event, here is an example:

const log = console.log;

const child = document.getElementById('child');
const parent = document.getElementById('parent');
const child2 = document.getElementById('child2');
const parent2 = document.getElementById('parent2');


parent.addEventListener('newColor', function(e) {
    if (e.defaultPrevented) return;
    log('parent')
    this.style.color = e.detail.textColor;
})

child.addEventListener('newColor', function(e) { 
    e.preventDefault();
    log('child')
    this.style.color = e.detail.textColor;
    this.style.backgroundColor = e.detail.bgrColor;
})

parent2.addEventListener('newColorNotCancelable', function(e) {
    if (e.defaultPrevented) return; // does not return as event is not cancelable
    log('parentNotCancelable')
    this.style.color = e.detail.textColor;
})

child2.addEventListener('newColorNotCancelable', function(e) { 
    e.preventDefault();
    log('childNotCancelable')
    this.style.color = e.detail.textColor;
    this.style.backgroundColor = e.detail.bgrColor;
})


function changeColor() { 
    const myEvent = new CustomEvent('newColor', { 
        detail: { 
            textColor: 'red',
            bgrColor: 'blue',
        },
        cancelable: true,
        bubbles: true
    })    
    child.dispatchEvent(myEvent)
    const myEventNotCancelable = new CustomEvent('newColorNotCancelable', { 
        detail: { 
            textColor: 'red',
            bgrColor: 'blue',
        },
        cancelable: false,
        bubbles: true
    })
    child2.dispatchEvent(myEventNotCancelable)
}


changeColor()
<p id="parent">this is the parent<span id="child"> THIS IS THE CHILD ELEMENT </span></p>
<p id="parent2">this is the parent<span id="child2"> THIS IS THE CHILD ELEMENT </span></p>

When you dispatch an event on child and child2, the event will propagate to their parents. By calling Event.preventDefault() on their children, you can make the parents know that the event has been "default prevented", if the Event is cancelable. It is similar to Event.stopPropagation() but it is more powerful because you can only skip certain DOM objects on the propagation chain.

Upvotes: 2

B_Joker
B_Joker

Reputation: 119

We need cancelable field just to signal to some event listener whether the event can be cancelled. Yep, this is it. The point is that preventDefault has not effect if called for some event e and e.cancelable === false.

Here's one example. Imagine that you develop some js-library that communicates the lib's user using events (i.e. it's designed per the EventBus pattern. )

The lib has a bunch of emitter functions. They emit custom events and execute some behaviour. Inside such emitter functions you might want to check whether the event emitted by the function was cancelled by some listener and change the function's behaviour accordingly. It may result to termination of the execution, some cleanup job, calling another function etc. What matters is that as a lib developer, you just let the user know what the lib does upon emitting some custom event e.

Now if the user wants to prevent the lib's behaviour he needs to subscribe for e and cancel it by calling e.preventDefault. Yes, you can call it at custom event. What it does is making the emitter's EventTarget.dispatchEvent() method return false if the e is cancelable (not limited by that behaviour but that's what important for our example).

So why do we need the cancelable field? It comes into play in some more sophisticated scenaniors. Imagine that your lib has some behaviour which can or cannot be stopped or modified under some but not every scenario. Your lib is a black box. How would you let the user know the exact case? Yep, e.cancelable === false. As I've mentioned, the point is that preventDefault has no effect if called upon non cancelable event. Also there's no error risen. So by signaling e.cancelable === false you let the lib's user know that he has to resolve that other than by calling e.preventDefault().

Upvotes: 0

Related Questions