Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stop propagation doesn't work

I have the below JQuery eventhandler. I want to stop all navigations on a web page.

$(document).click(function(event) {
            event.stopPropagation();
            event.preventDefault();
            event.cancelBubble = true;
            event.stopImmediatePropagation();
            $(document).css('border-color','');
            $(document).css('background-color',''); 
            $(event.target).css('border-color','yellow');
            $(event.target).css('background-color','#6BFF70');
            return false;
    });

When I use this on Facebook Login page, it stops all navigations. But in Google home page, "I'm Feeling Lucky" button still navigates to next page. How do I avoid it?

I'm using JavaFX browser by the way. It is similar to Safari browser.

like image 693
NaveenBharadwaj Avatar asked Jul 21 '15 09:07

NaveenBharadwaj


4 Answers

If I load the Google search page, and execute this at the console:

document.body.addEventListener(
  "click", 
  function (ev) { ev.stopPropagation(); ev.preventDefault(); },
  true);

then I cannot click the "I'm Feeling Lucky" button anymore. The key is to use the third parameter and set it to true. Here is what MDN [says] about it:

useCapture Optional

If true, useCapture indicates that the user wishes to initiate capture. After initiating capture, all events of the specified type will be dispatched to the registered listener before being dispatched to any EventTarget beneath it in the DOM tree.

(Emphasis added.)

What you tried to do does not work because your event handler is on document, and thus will be called after any event handlers on the children of the document. So your handler cannot prevent anything.

With useCapture set to true, you can operate on the event before it gets a chance to be passed to the child element. I do not know of a way to have jQuery's event handlers work in the way you get with useCapture. Barmar's answer here says you can't use jQuery to set such handler. I'm inclined to believe him.

like image 80
Louis Avatar answered Oct 05 '22 06:10

Louis


99.99% of webpages won't be able to have their navigation stopped by stopping event propagation for the reason I commented (you can't stop the event before it triggers all handlers for the initial target of the event). If preventing navigation is all you are interested in, I recommend using the window.onbeforeunload event, which is made for this exact situation.

Here is an example: http://jsfiddle.net/ejreseuu/

HTML:

<a href="google.com">google</a>

JS:

window.onbeforeunload = function() {
    return "Are you sure?"
}

There is no way to not have a confirmation box that I know of, as code that locks the user out of navigating away no matter what they do is generally malicious.

like image 33
Tahsis Claus Avatar answered Oct 05 '22 07:10

Tahsis Claus


preventDefault() should not work in this case, cause Google relied on custom event listeners to handle click events on this button. While preventDefault() prevents browser's default behavior.

For example, if this button was of type="submit", preventing default on click event would prevent browser's default behavior, which is submitting a form. But in this case click is handled by eventListeners added to the button itself. preventDefault() won't affect catching an event by them. Nor stopPropagation(), because it stops propagation of event to higher levels of DOM, while other eventListeners on the same level (button in our case) still get the event. stopImmediatePropagation() could work in theory, but only if your eventListener was added before google's.

So the easiest way to stop propagation is to stop an event before it reaches button node, and that's on capture phase, because button is the lowest element in the hierarchy. This can be done by passing true argument while adding eventListener

document.body.addEventListener("click", function (event) { 
  event.stopPropagation();
}, true);

This way event will be stopped before bubble phase, and so before it reaches eventListeners added to the button. More on capture and bubble phases here

Note that preventDefault() is not needed in this case. Actually, this button's event listeners are to prevent default themselves. Here are those eventListeners, for click and keyup respectively:

d = function(a) {
 c.Xa.search(c.yc(), b);
 return s_1vb(a)
}

function(a) {
 13 != a.keyCode && 32 != a.keyCode || d(a)
}

note call to s_1vb, here is its sourse:

s_1vb.toString();
/*"function (a){
     a&&(a.preventDefault&&a.preventDefault(),a.returnValue=!1);
     return!1
  }"*/

Basically its a function that take an event and do everything possible to prevent browser's default behavior

By the way, default behavior can be canceled on any stage of event flow (se Events Specification), including the very last stage, when it reached document. Only after it passed "through" all eventListeners uncanceled, browser should execute its default behavior. So attaching your listener to document was not the reason preventDefault() didn't work, it was because it was the wrong guy for the job :)

like image 2
Pavlo D Avatar answered Oct 05 '22 07:10

Pavlo D


Try this:

$('body').click(function(event) {
            event.stopPropagation();
            event.preventDefault();
            event.cancelBubble = true;
            event.stopImmediatePropagation();
            $(document).css('border-color','');
            $(document).css('background-color',''); 
            $(event.target).css('border-color','yellow');
            $(event.target).css('background-color','#6BFF70');
            return false;
    });
like image 1
kimwz.kr Avatar answered Oct 05 '22 06:10

kimwz.kr