In the previous chapter we explored how a browser can render our code into a page. We looked at the rendering process and synthesizers. In this article we will explore how the synthesizer responds smoothly to user input.

Input events from the browser’s perspective

Normally, we only think of input events as input boxes or mouse clicks, but from the browser’s point of view any gesture is input. Mouse scroll, touch, and mouse hover are all input events to the browser.

When the user touches the screen, the browser process is the first to receive the gesture event. However, the browser process only knows where the event occurred, and cannot directly respond to the user’s actions, because the contents of the TAB are handled by the rendering process. So the browser process sends the event type (such as TouchStart) and the coordinates of where the event occurred to the renderer process. After receiving an event, the rendering process finds the corresponding event object and executes the corresponding event listener.

Input events passed by the browser process to the rendering process

Synthesizer received input event

Viewport hover over the page layer

In the previous chapter, we discussed how the synthesizer smoothed the scrolling by creating rasterized layers. If the current page has no event listener binding, the compositing thread creates a new compositing frame that is completely independent of the main thread. But what if the page is bound to an event listener? How does the synthesizer thread know if an event needs to be processed?

Understanding non-fast Scrollable Regions

Since it is the main thread’s job to run the JS code, when the page is synthesized, the compositing thread marks the part of the page bound to the event listener as a “non-fast Scrollable Region”. When an event in this region is triggered, the tag composing thread ensures that the input event is sent to the main thread. If the input event comes from outside this area, the compositing thread will continue to compose new frames without waiting for the main thread.

Input events from a non-fast scrolling region

Write precautions for event listening

Event delegates are often used when writing event listeners. Event bubbling allows you to listen for events on top elements.

document.body.addEventListener('touchstart'.event= > {
    if(event.target === area) { event.preventDefault(); }});Copy the code

The advantage of this is that when there are multiple elements that need to be bound to the same event, there is no need to bind the event to each element individually. But doing so from the browser’s point of view can result in unnecessary areas being marked as non-fast scrolling. If we don’t care about the input events in other areas of the page, the composite thread still has to wait for the main thread to finish processing the events when they occur. The synthesizer’s ability to provide a smooth user experience is thus compromised.

The whole area is a non-fast scrolling area

You can use passive: true to tell the browser that the main thread will continue to listen for the event when it happens. The compositing thread will stop waiting and continue to compose new frames.

document.body.addEventListener('touchstart'.event= > {
    if (event.target === area) {
        event.preventDefault()
    }
 }, {passive: true});
Copy the code

Finding event objects

The main thread looks at the drawing record and asks what was drawn at x and y

When the synthesizer thread sends an input event to the main thread, the first thing to run is a hit test to find the event target. The hit test uses the draw record data generated during rendering to find the content corresponding to the coordinates of the event that occurred.

Reduce the number of events on the main thread that sends all messages

We know that the device refresh rate is usually around 60 times per second, and the page rendering rate is consistent with that and we can get smooth animation. But for input events, a typical touchscreen device transmits 60-120 touch events per second, and a mouse transmits 100 events per second, the frequency of input events is higher than the refresh rate of the device.

If a continuous event such as touchMove is sent to the main thread at a rate of 120 times per second, it may trigger too many hit tests and JavaScript executions compared to the screen refresh rate.

Too many trigger events cause the page to lag

In order to reduce the main thread of excessive calls, Chrome will continuous events (for example: the wheel, mousewheeel, mousemove, pointermove, touchmove) and will schedule delayed until the next requestAnimationFrame.

Merge and delay sending

Discontinuous events such as keyDown, keyUp, mouseUp, mouseDown, TouchStart, and TouchEnd are sent immediately.

usegetCoalescedEventsGets intra-frame events

For most applications, composite events are sufficient to provide a good user experience. However, if you are developing a drawing application, you need to place a path based on touchMove coordinates and things like that. If you use synthetic events, you may lose some of the coordinates in the middle, making it difficult to draw a smooth curve. In this case, you can use the getCoalescedEvents method in the Pointer event to get information about the merge events.

On the left is the effect after use and on the right is the effect before use

window.addEventListener('pointermove'.event= > {
    const events = event.getCoalescedEvents();
    for (let event of events) {
        const x = event.pageX;
        const y = event.pageY;
        // draw a line using x and y coordinates.}});Copy the code

One day

useLighthouse

Use the Lighthouse tool. Lighthouse will evaluate your site, figure out what’s going well and what needs to be improved, and present it as a report. Combined with the report, some targeted optimization operations can be done.

Evaluating site performance

Different websites require different optimization strategies. So learn to evaluate your site’s performance bottlenecks and find out what you need to optimize. Refer to how to measure your Site’s performance

Adding a Function Policy

Further, take a look at Feature Policies, a new Web platform Feature that provides some protection when you build your project to make your application behave and prevent you from making mistakes. For example, if you want to ensure that your application code does not block page parsing, you can run your application in a synchronous scripting policy. This is done by setting sync-script to ‘None’ so that JavaScript code that blocks page parsing is banned. This has the advantage of preventing your code from blocking the parsing of the page, and the browser doesn’t have to worry about parser pauses.

The resources

inside-browser-part4