Browser Default Action

Many events automatically lead to browser actions. For instance -

If we handle an event in JavaScript, often we don’t want browser actions. Fortunately, it can be prevented.

Here are some situation where this feature is used very commonly -

Preventing browser actions

There are two ways to tell the browser we don’t want it to act:

For example, the following two links won't lead you to the url -

<a href="/" onclick="return false">Click here</a>
or
<a href="/" onclick="event.preventDefault()">here</a>
Click here or here

It doesn't mean that you need to return true

The value returned by an event handler is usually ignored. The only exception – is return false from a handler assigned using on<event>. In all other cases, the return is not needed and it’s not processed anyhow.

Example - Menu

Consider the following example. In the following example We are preventing the browser's default action. But you will still be able to open it in new tab by right clicking on the link. If it were buttons instead of links, opening in new tab wasn't possible. So the following approach servs the following benefit -

<style type="text/css">
.demo1{
	list-style: none;
}
.demo1>li{
	display: inline-block;
	margin-right: 10px;
}
.demo1>li a{
	padding: 10px;
	border: 1px solid #333;
	background: violet;
	color: #fff;
}
</style>
<ul id="menu" class="menu">
  <li><a href="/html">HTML</a></li>
  <li><a href="/javascript">JavaScript</a></li>
  <li><a href="/css">CSS</a></li>
</ul>

<script type="text/javascript">
document.getElementById("demo1").onclick = function(event){
	if (event.target.tagName != 'A') {
		return;
	}
	alert(event.target.textContent); // Do your own action here --
	return false;
};
</script>

If we omit return false, then after our code executes the browser will do its “default action” – following to the URL in href.

Example - "return false;"

The following example doesn't work -

<script>
function handler1() {
  alert( "..." );
  return false;
}
</script>

<a href="http://w3.org" onclick="handler1()">the browser will go to w3.org</a>
the browser will go to w3.org

When the browser reads the on* attribute like onclick, it creates the handler from its content.

For onclick="handler()" the function will be:

function(event) {
  handler() // the content of onclick
}

Now we can see that the value returned by handler() is not used and does not affect the result. Here is the solution -

<script>
  function handler() {
    alert("...");
    return false;
  }
</script>

<a href="http://w3.org" onclick="return handler()">w3.org</a>

You can also use event.preventDefault() like this -

<script>
  function handler(event) {
    alert("...");
    event.preventDefault();
  }
</script>

<a href="http://w3.org" onclick="handler(event)">w3.org</a>
w3.org

Prevent Further Events

Certain events flow one into another. If we prevent the first event, there will be no second.

For instance, mousedown on an <input> field leads to focusing in it, and the focus event. If we prevent the mousedown event, there’s no focus.

Try to click on the first <input> below – the focus event happens. That’s normal. But if you click the second one, there’s no focus.

<input value="Focus works" onfocus="this.value=''">
<input onmousedown="return false" onfocus="this.value=''" value="Click me">

That’s because the browser action is canceled on mousedown. The focusing is still possible if we use another way to enter the input. For instance, the Tab key to switch from the 1st input into the 2nd. But not with the mouse click any more.

event.defaultPrevented

The property event.defaultPrevented is true if the default action was prevented, and false otherwise. There’s an interesting use case for it.

You remember in the chapter Bubbling and capturing we talked about event.stopPropagation() and why stopping bubbling is bad?

Sometimes we can use event.defaultPrevented instead.

Let’s see a practical example where stopping the bubbling looks necessary, but actually we can do well without it.

By default the browser on contextmenu event (right mouse click) shows a context menu with standard options. We can prevent it and show our own, like this:

<button>Right-click for browser context menu</button>

<button oncontextmenu="alert('Draw our menu'); return false">
  Right-click for our context menu
</button>

Now let’s say we want to implement our own document-wide context menu, with our options. And inside the document we may have other elements with their own context menus:

<p>Right-click here for the document context menu</p>
<button id="elem">Right-click here for the button context menu</button>

<script>
  elem.oncontextmenu = function(event) {
    event.preventDefault();
    alert("Button context menu");
  };

  document.oncontextmenu = function(event) {
    event.preventDefault();
    alert("Document context menu");
  };
</script>

Right-click here for the document context menu

The problem is that when we click on elem, we get two menus: the button-level and (the event bubbles up) the document-level menu.

How to fix it? One of solutions is to think like: “We fully handle the event in the button handler, let’s stop it” and use event.stopPropagation():

<p>Right-click for the document menu</p>
<button id="elem">Right-click for the button menu (fixed with event.stopPropagation)</button>

<script>
  elem.oncontextmenu = function(event) {
    event.preventDefault();
    event.stopPropagation();
    alert("Button context menu");
  };

  document.oncontextmenu = function(event) {
    event.preventDefault();
    alert("Document context menu");
  };
</script>

Right-click for the document menu

Now the button-level menu works as intended. But the price is high. We forever deny access to information about right-clicks for any outer code, including counters that gather statistics and so on. That’s quite unwise.

An alternative solution would be to check in the document handler if the default action was prevented? If it is so, then the event was handled, and we don’t need to react on it.

<p>Right-click for the document menu (fixed with event.defaultPrevented)</p>
<button id="elem">Right-click for the button menu</button>

<script>
  elem.oncontextmenu = function(event) {
    event.preventDefault();
    alert("Button context menu");
  };

  document.oncontextmenu = function(event) {
    if (event.defaultPrevented) return;

    event.preventDefault();
    alert("Document context menu");
  };
</script>

Right-click for the document menu (fixed with event.defaultPrevented)

Now everything also works correctly. If we have nested elements, and each of them has a context menu of its own, that would also work. Just make sure to check for event.defaultPrevented in each contextmenu handler.

event.stopPropagation() and event.preventDefault()

These two are different thing. event.stopPropagation() stops the bubbling. And event.preventDefault() doesn't stop bubblinng, instead it prevents the default browser action.