Events
The XTerminal class, from which we create an instance, extends an internal EventEmitter class. This implies that we can handle events the same way the browser does to provide interaction through events like: click, keydown, and so on.
The underlying EventEmitter exposes, the on
, off
, once
, and emit
methods.
on
is used to add an event listener that's executed when the event is triggeredoff
is used to remove an event listener from an eventonce
is used to add a one-time event listener, it is triggered only once and then removed usingoff
emit
is used to trigger an event
Custom Events
Create a start
event, and as a matter of providing an example, the reaction to the event is a simply outputting to the terminal.
term.on('start', () => {
term.writeln('started...');
});
When we run the emit
method passing the start
event,
term.emit('start');
the event handler function is triggered, and we get the terminal log.
Arguments
You can pass multiple arguments to the event handler by passing them as additional arguments to term.emit()
.
term.on('start', (id) => {
term.writeln('started...', id);
});
term.emit('start', 5173);
Example with multiple arguments:
term.on('start', (start, end) => {
term.writeln(`started from ${start} to ${end}`);
});
term.emit('start', 1, 10);
One-Time Event
In some cases, it might be necessary to only run an operation once and only once. Any event listener added using the term.once()
method is executed once and deleted thereafter when the event is triggered.
term.once('load', () => {
term.writeln('loaded...');
});
term.emit('load');
term.emit('load');
The load
event is triggered and will output to the terminal for the first term.emit('load')
. The second event trigger does nothing since there is no event listener for the load
event anymore.
Symbols
Apart from strings, JavaScript symbols can as well be used to create events too.
const START_EVENT = Symbol('start');
term.on(START_EVENT, () => {
term.writeln('started with a symbol...');
});
term.emit(START_EVENT);
Default Events
Every terminal instance has existing events that are used internally and can be used in your application lifecycle. They include:
data
event - triggered when user inputs data and presses the Enter keyclear
event - triggered on term.clear()keypress
event - triggered on every key press except Tab, Enter, ArrowUp and ArrowDownpause
event - triggered on term.pause(), when the terminal input is deactivated or pausedresume
event - triggered on term.resume(), when the terminal input is activated or resumed
Example
In this example, you are going to capture the user's input and simply write it to the terminal.
First, add an event listener for the data
event to capture data, output it and then ask for more input thereafter. Clear the terminal on recieving the input matching to clear
and as a result, everything is erased from the terminal including the prompt style. Additionally, add a keypress
event to clear the terminal.
Code
term.on('data', (input) => {
if (input == 'clear') {
// clear the terminal
term.clear();
} else {
// do something
term.writeln('Data: ' + input);
}
// write the prompt again
term.write("$ ");
});
term.on('clear', () => {
term.writeln('You cleared the terminal');
});
term.on('keypress', (ev) => {
/**
* Checkout the event object
*/
console.log(ev);
// on CTRL+L - clear
if (ev.key.toLowerCase() == 'l' && ev.ctrlKey) {
// prevent default behaviour
ev.cancel();
// clear and trigger `clear` event
term.clear();
}
});
The terminal will be cleared incase the user inputs clear
or presses the shortcut CTRL+L
which triggers the clear
event that logs You cleared the terminal
on the screen.
Limitations
Multiple events can exist on the same terminal instance which is an advantage. However you should keep caution on when every event is triggered.
Nested Emits
When an event is triggered, it is added on top of the emitting stack and then the listeners attached to the event are invoked synchronously. If you emit the same event within one of the listeners, it will not work.
Example:
The code sample below will not work as expected.
term.on('run', () => {
console.log('running...');
// ...
term.emit('run');
});
Triggering the event run
will log in the console: running...
, do stuff, and attempt to trigger itself again (possible deadlock).
Workaround
Trigger the same event in the next event loop.
term.on('run', () => {
console.log('running...');
// ...
setTimeout(() => term.emit('run'), 0);
});
Next Step
You'll learn to everything about the prompt including activation, styling, blur and focus.