Appearance
Events
Because JavaScript runs in a single-threaded mode in browsers, once all JavaScript code on a page is executed after loading, further execution relies on event triggers.
When the browser receives mouse or keyboard input from the user, it automatically triggers the corresponding event on the relevant DOM node. If a JavaScript handler is bound to that node, it is invoked automatically.
Due to differences in how various browsers bind events, using jQuery allows us to write code that abstracts these differences, enabling consistent code writing.
For example, to display an alert when a user clicks a hyperlink, we can bind a click event with jQuery like this:
javascript
/* HTML:
*
* <a id="test-link" href="#0">Click me</a>
*
*/
// Get the jQuery object for the hyperlink:
let a = $('#test-link');
a.on('click', function () {
alert('Hello!');
});
The on
method binds an event, requiring the event name and the handler function.
An alternative, more concise method is to directly call click()
:
javascript
a.click(function () {
alert('Hello!');
});
Both approaches are equivalent, but we typically prefer the latter.
Events that jQuery can bind primarily include:
Mouse Events
click
: Triggered on mouse click.dblclick
: Triggered on mouse double-click.mouseenter
: Triggered when the mouse enters.mouseleave
: Triggered when the mouse leaves.mousemove
: Triggered when the mouse moves within the DOM.hover
: Triggers two functions when the mouse enters and exits, equivalent tomouseenter
plusmouseleave
.
Keyboard Events
Keyboard events apply only to the currently focused DOM, usually <input>
and <textarea>
.
keydown
: Triggered when a key is pressed.keyup
: Triggered when a key is released.keypress
: Triggered after a key press.
Other Events
focus
: Triggered when the DOM gains focus.blur
: Triggered when the DOM loses focus.change
: Triggered when content in<input>
,<select>
, or<textarea>
changes.submit
: Triggered when a<form>
is submitted.ready
: Triggered when the page is loaded and the DOM tree is initialized.
The ready
event only applies to the document object. Since it triggers after the DOM initialization and only once, it's ideal for writing other initialization code. For instance, to bind a submit
event to a <form>
, the following code won't work as expected:
html
<html>
<head>
<script>
// Code has issues:
$('#testForm').on('submit', function () {
alert('submit!');
});
</script>
</head>
<body>
<form id="testForm">
...
</form>
</body>
This is because the <form>
hasn't loaded in the browser when JavaScript executes, so $('#testForm')
returns an empty array, and no event is bound.
Thus, our initialization code must reside within the document's ready event to ensure the DOM is fully initialized:
html
<html>
<head>
<script>
$(document).on('ready', function () {
$('#testForm').on('submit', function () {
alert('submit!');
});
});
</script>
</head>
<body>
<form id="testForm">
...
</form>
</body>
This code functions correctly because it executes after the DOM tree initialization.
Due to the common use of the ready event, it can be simplified to:
javascript
$(document).ready(function () {
// The on('submit', function) can also be simplified:
$('#testForm').submit(function () {
alert('submit!');
});
});
It can even be further simplified to:
javascript
$(function () {
// init...
});
This form is the most common. If you encounter $(function () {...})
, remember it's the document's ready event handler.
You can repeatedly bind event handlers, and they will execute in sequence:
javascript
$(function () {
console.log('init A...');
});
$(function () {
console.log('init B...');
});
$(function () {
console.log('init C...');
});
Event Parameters
For events like mousemove
and keypress
, we often need to get the mouse position and the key value; otherwise, listening to these events is pointless. Every event passes an Event object as a parameter, providing more information:
javascript
$(function () {
$('#testMouseMoveDiv').mousemove(function (e) {
$('#testMouseMoveSpan').text('pageX = ' + e.pageX + ', pageY = ' + e.pageY);
});
});
Unbinding Events
An already bound event can be unbound using off('click', function)
:
javascript
function hello() {
alert('hello!');
}
a.click(hello); // Bind event
// Unbind after 10 seconds:
setTimeout(function () {
a.off('click', hello);
}, 10000);
It’s crucial to note that the following approach is ineffective:
javascript
// Bind event:
a.click(function () {
alert('hello!');
});
// Unbind:
a.off('click', function () {
alert('hello!');
});
Despite having the same appearance, these two anonymous functions are different function objects, and off('click', function () {...})
cannot remove the first one.
To remove it effectively, you can use off('click')
to remove all bound click event handlers.
Similarly, calling off()
without parameters removes all types of event handlers that are bound.
Event Trigger Conditions
One point to note is that event triggers are always initiated by user actions. For instance, when monitoring changes in a text box:
javascript
let input = $('#test-input');
input.change(function () {
console.log('changed...');
});
The change event is triggered when the user inputs something in the text box. However, if the text box value is changed using JavaScript, the change event won’t be triggered:
javascript
let input = $('#test-input');
input.val('change it!'); // Will not trigger change event
Sometimes, we want to trigger the change event programmatically, which can be done by calling the parameterless change()
method:
javascript
let input = $('#test-input');
input.val('change it!');
input.change(); // Triggers change event
The input.change()
is equivalent to input.trigger('change')
, a shorthand for the trigger() method.
Why might we want to manually trigger an event? Without doing so, we often end up writing two identical pieces of code.
Exercise
For the following form:
html
<!-- HTML structure -->
<form id="test-form" action="test">
<legend>Please select the programming language you want to learn:</legend>
<fieldset>
<p><label class="selectAll"><input type="checkbox"> <span class="selectAll">Select All</span><span class="deselectAll">Deselect All</span></label> <a href="#0" class="invertSelect">Invert Selection</a></p>
<p><label><input type="checkbox" name="lang" value="javascript"> JavaScript</label></p>
<p><label><input type="checkbox" name="lang" value="python"> Python</label></p>
<p><label><input type="checkbox" name="lang" value="ruby"> Ruby</label></p>
<p><label><input type="checkbox" name="lang" value="haskell"> Haskell</label></p>
<p><label><input type="checkbox" name="lang" value="scheme"> Scheme</label></p>
<p><button type="submit">Submit</button></p>
</fieldset>
</form>
Bind appropriate event handlers to achieve the following logic:
- When the user checks "Select All," automatically select all languages and change "Select All" to "Deselect All."
- When the user unchecks "Deselect All," automatically uncheck all languages.
- When the user clicks "Invert Selection," automatically invert the selection of all languages (checked becomes unchecked, unchecked becomes checked).
- When all languages are manually checked, "Select All" is automatically checked and changes to "Deselect All."
- When the user manually unchecks at least one language, "Deselect All" is automatically unchecked and changes to "Select All."
javascript
let
form = $('#test-form'),
langs = form.find('[name=lang]'),
selectAll = form.find('label.selectAll :checkbox'),
selectAllLabel = form.find('label.selectAll span.selectAll'),
deselectAllLabel = form.find('label.selectAll span.deselectAll'),
invertSelect = form.find('a.invertSelect');
// Reset initial state:
form.find('*').show().off();
form.find(':checkbox').prop('checked', false).off();
deselectAllLabel.hide();
// Intercept form submit event:
form.off().submit(function (e) {
e.preventDefault();
alert(form.serialize());
});
// TODO: Bind events
// Test:
console.log('Please test if the functionality is working correctly.');