Q:

Implementation of event listener - assembly vs high level language

Assembly:
Consider the Intel 8080. To handle an interrupt (for example from the keyboard), you simply have the keyboard pull the 'INT' pin high and place a 'vector' on the databus. The vector points to the location of the interrupt service routine (ISR) in the assembly program code. See this answer for more details.
High level language:
Consider the concept of attaching event listeners. For example consider Javascript's keydown event listener. An example:
document.getElementById("demo").addEventListener("keydown", myFunction);

function myFunction() {
    document.getElementById("demo").style.backgroundColor = "red";
}

When a key is pressed down while the 'demo' element is in focus, myFunction gets called.
Relationship:
How does the concept of attaching event listeners relate to the concept of having ISRs in assembly?
I assume that the function that directly responds to a keyboard interrupt is 1) part of the OS kernel and 2) written in C. (Are these assumptions correct?)
How does a user program tell this OS interrupt handler to alert it upon interrupt? Does the OS function keep a variable size list of functions to callback when it handles an interrupt? Does then addEventListener append the user callback function to the OS function's list?
Note: I chose the Intel 8080 as an example specifically because in contrast to modern CPUs, it does not have fancy capabilities to aid with OS related functionality.

A:

It's a long and complex journey from a key being pressed and the code in a JavaScript keydown event listener being run.  Assuming a USB keyboard, the path through hardware and software layers is something like this:

keyboard (hardware layer)
USB controller (hardware layer)
USB controller driver (kernel layer)
USB generic driver (kernel layer)
HID generic driver (kernel layer)
HID keyboard driver (kernel layer)
OS UI event processing (kernel/user layer)
browser UI event loop (user layer)
browser UI event handler (user layer)
JavaScript engine event handler (user layer)
keydown event listener (user layer)

Most of this doesn't work how you're probably assuming. In particular there's no keyboard interrupt. Instead there's a USB interrupt but it doesn't quite work the way you might assume. When you press a key on USB keyboard it doesn't result in a message being sent to the computer and an interrupt generated.  Instead the keyboard adds the key press to its internal queue and waits for the computer to poll it.
This is because USB communication is entirely scheduled by the USB host (the computer). USB devices aren't allowed to talk on the bus except in response to a request by the host. As USB transactions are scheduled into one millisecond frames, normally an operating system will only poll a USB keyboard once a millisecond, asking it to report any events that have occurred since it last polled the device. Only once the keyboard responds to this request (or maybe when all the scheduled transfers in the frame have completed), will the USB controller generate an interrupt.
The response from the keyboard will be in the form a HID (Human Interface Device) report. The HID stack will decode it to see what keyboard events have been reported, converting them into a format common to all keyboard types. This will be further processed by some sort of user-interface layer in the operating system (eg. The "Win32" API layer on Windows, or an X Server on Linux) and then put on the UI event queue for the browser.
The browser will not be "interrupted" as a result of the key being pressed. Instead the browser will have a main UI interface event loop and events will only be processed one a time at a single defined point in the program. All this loop does is pull events from the UI event queue and dispatches them to the appropriate code in the browser. When the queue is empty, which is normally almost all of the time, the loop simply waits for an event. While it's waiting, the event loop's thread is not scheduled by the OS and is not running on any CPU.
Once the UI event loop gets a keyboard event, it's passed onto the browser's keyboard event handler which will then pass it to the JavaScript engine. The engine will then execute the function assigned as the keydown event listener. The engine may have its own event queue, as JavaScript events are normally also only processed one at a time.
None of this will be written in assembly except a tiny amount of code in the kernel that acts as a landing point for interrupts and system calls before calling the C code that actually dispatches them.

