English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Native JS Control Multiple Scrollbars to Sync Follow Scroll Effect

On some websites that support writing articles in markdown, the background writing page usually supports markdown real-time preview, that is, the entire page is divided into two parts: the left part is the markdown text you input, and the right part outputs the corresponding preview page in real time. For example, the following is the markdown real-time preview effect of the CSDN background writing page:

This article does not explain how to implement this effect from scratch (to be continued) It is very likely This article will single out the article, leaving aside other things, let's just look at the two container elements in the main body of the page, that is, the markdown input box element and the preview display box element

This article discusses how to make one container element scroll while the other element also scrolls when both container elements' contents exceed the container height, that is, when both appear as scrollbars.

DOM Structure

Since it is related to the scrollbar, the first thing that comes to mind is a property in JavaScript that controls the scrollbar height: scrollTop. As long as you can control the value of this property, you can naturally control the scrolling of the scrollbar.

For the following DOM structure:

<div id="container">
 <div class="left"></div>
 <div class="right"></div>
</div>

Among them, the .left element is the container element for the left half input box, the .right element is the container element for the right half display box, and .container is their common parent element.

Since overflow scrolling is needed, it is also necessary to set the corresponding styles (only the key styles, not all):

#container {
 display: flex;
 border: 1px solid #bbb;
}
.left, .right {
 flex: 1;
 height: 100%;
 word-wrap: break-word;
 overflow-y: scroll;
}

Add enough content to the .left and .right elements to make them appear with scrollbars, like this:

The style is more or less in place, so we can perform a series of operations on these DOM elements.

Initial attempt

The general idea is to listen to the scroll events of the two container elements. When one element scrolls, get the value of its scrollTop property and set this value as the scrollTop value of the other scrolling element.

For example:

var l = document.querySelector('.left')
var r = document.querySelector('.right')
l.addEventListener('scroll', function() {
 r.scrollTop = l.scrollTop
)

The effect is as follows:

It seems quite good, but now I want not only the right side to follow the left side's scrolling but also the left side to follow the right side's scrolling, so I add the following code:

addEventListener('scroll', function() {
 r.scrollTop = l.scrollTop
)

It looks quite good, but, things are not that simple.

At this point, when you use the mouse wheel to scroll, you find that it is quite laborious, as if something is hindering the scrolling of the two container elements, making it difficult to scroll.

An in-depth analysis reveals that the reason is simple. When you scroll to the left, it triggers the scroll event on the left, causing the right side to follow the scroll. However, at the same time, the right side's following scroll also triggers the scroll event on the right, causing the left side to follow the right side...and then it enters a situation similar to mutual triggering, making the scrolling feel very laborious.

Solve the problem of simultaneous triggering of scroll events

To solve the above problems, there are temporarily two solutions.

Replace the scroll event with the mousewheel event

Since the scroll event can be triggered not only by the active scrolling of the mouse but also by changing the scrollTop of the container element, the active scrolling of the element is actually triggered by the mouse wheel. Therefore, the scroll event can be replaced with an event sensitive to mouse scrolling rather than element scrolling: 'mousewheel', so the above listening code becomes:

addEventListener('mousewheel',function(){
  r.scrollTop = l.scrollTop
)
r.addEventListener('mousewheel',function(){
  l.scrollTop = r.scrollTop
)

The effect is as follows:

It seems to be useful, but in fact there are still two problems.

When one container element is scrolled, the other container element also follows the scroll, but the scroll is not smooth, and there is a significant bounce in height

I searched online and couldn't find any content related to the scrolling frequency of the wheel event. I speculate that this may be a feature of this event.

The mouse does not scroll with the same frequency each time 1px as the unit, its smallest unit is much smaller than that of the scroll event. I roll the mouse on the chrome browser, and the distance I roll each time is exactly 100px, and this value should be different for different mice or browsers. The wheel event actually listens to the event that the mouse wheel rolls over a gear detent, which can explain why the bouncing phenomenon occurs.

Generally speaking, each time the mouse wheel rolls over a gear detent, a wheel event can be detected, from the beginning to the end, the element actively rolled by the mouse has already scrolled 100px, so the other container element following the scroll will also jump instantly 100px

The reason why the aforementioned scroll event does not cause the following scroll element to bounce instantly is that each time the scrollTop value of the following scroll element changes, its value will not have 10A span of 0px as large as that may not be small enough 1px, but due to its high trigger frequency and small scroll range, it is visually smooth scrolling at least.

The 'wheel' event only listens to mouse wheel events, but if you drag the scrollbar with the mouse, this event will not be triggered, and the other container elements will not follow the scroll.

This can actually be solved quite easily; dragging the scrollbar with the mouse will definitely trigger the scroll event. In this case, you can easily determine which container element the dragged scrollbar belongs to. You only need to handle the scroll event of this container, and no need to process the scroll event of the other container following the scroll.

wheel event compatibility issues

wheel event is DOM Level3 of the standard events, but in addition to this event, there are many non-standard events, and different browser cores use different standards, so it may be necessary to make compatibility according to the situation, for more information, see MDN MouseWheelEvent

Real-time judgment

If you cannot bear the jump of wheel and various compatibility issues, there is actually another way to go, which is still the scroll event, but it requires some additional work.

The problem with the scroll event is that it does not determine which container element is currently actively scrolling. Once the active scroll container element is determined, the matter is easily resolved, for example, in the aforementioned use of the wheel event, the reason why the scroll event can be used when the scrollbar is dragged with the mouse is that it can easily determine which container element is the active scroll container element.

Therefore, the key issue lies in how to determine the current active scroll container element. Once this problem is solved, the rest is easy to handle.

Whether it is the scrolling of the mouse wheel or the dragging of the scrollbar by pressing the mouse, it will trigger the scroll event, and at this time, the mouse's coordinates are definitely within the area occupied by the scroll container element on the Z-axis, which means that on the Z-axis, the mouse is hovering or located above the scroll container element.

It is possible to obtain the current coordinates of the mouse when it moves on the screen.

In this case, clientX and clientY are the coordinates of the current mouse relative to the viewport. It can be considered that as long as this coordinate is within the range of a certain scroll container, it is considered that the container element is the active scroll container element. The coordinate range of the container element can be obtained using getBoundingClientRect.

The following is an example code when the mouse moves over the .left element:

if (e.clientX>l.left && e.clientX<l.right && e.clientY>l.top) {
  // Enter the .left element
}

This is indeed possible, but considering that the two scroll container elements almost occupy the entire screen area, the area that mousemove needs to listen to is too large, which may have high performance requirements. Therefore, it is actually better to use the mouseover event, which only needs to listen to whether the mouse enters a certain scroll container element, thus saving the above coordinate judgment as well.

addEventListener('mouseover',function(){
 // Enter the .left scroll container element
)

When it is determined which container element is actively scrolled by the mouse, it is only necessary to handle the scroll event of this container, and the scroll event of the other following container does not need to be handled.

Well, the effect is very good, and the performance is also good, perfect, we can finish the job~

Proportional scrolling

All the above examples are the effects under the condition that the content heights of the two scrollable container elements are completely consistent. What if the content heights of these two scrollable container elements are different?

That is the following effect:

It can be seen that due to the different content heights of the two scrollable container elements, the maximum scrollTop is also different, so when one element with a smaller scrollTop value has scrolled to the bottom, the other element may still be halfway, or when one element with a larger scrollTop value has only scrolled halfway, the other element has already reached the bottom.

This situation is very common, for example, when you write in markdown, the height occupied by a first-level title marked with # in the editing mode is generally less than the height occupied in the preview mode, which results in the situation of inconsistent scroll heights on both sides.

Therefore, if this situation is also taken into consideration, then it cannot be as simple as setting the scrollTop value for two scrollable container elements to each other.

Although it is impossible to fix the height of the scrollable container content, it is certain that the maximum scroll height of the scrollbar, or the value of scrollTop, must be related to the height of the scrollable container content and the height of the scrollable container itself.

Since it is necessary to know the height of the scrollable container content and there is a scrollbar, it is necessary to add a child element to this container element. The height of the child element is not limited, just the height of the scrollable container content. The container height is fixed, and the overflow can be scrolled.

<div id="container">
 <div class="left">
	 <div class="child"></div>
 </div>
 <div class="right">
	 <div class="child"></div>
 </div>
</div>

The structural example is as follows:

Through my observations, inferences, and practical verification, the relationship between them has been determined. It is very simple, just the most basic addition and subtraction operations:

The maximum scroll height of the scrollbar (scrollTopMax) = the height of the scrollable container content (i.e., the height of the child element ch) - The height of the scrollable container itself (i.e., the height of the container element ph)

That is to say

That is to say, if the height of the scrollable container content (i.e., the height of the child element ch) and the height of the scrollable container itself (i.e., the height of the container element ph) have been determined, then it is certain that the maximum scroll height of the scrollbar (scrollTop) can be determined. Both of these height values are basically obtainable, so scrollTop can be obtained.

Therefore, in order to make two scroll element containers scroll up and down in proportion, that is, when one element scrolls to the top or bottom, the other element can also scroll to the top and bottom correspondingly, then you only need to get the maximum value of scrollTop between the two scroll container elements (scale).

After determining the scale, when scrolling in real time, you only need to get the scrollTop of the active scroll container element1 , then you can get the scrollTop of another container element that follows the scroll2 :

Once the ideas are clear, coding becomes an easy thing, and the effect is as follows:

Very smooth~

Summary

The above requirements have been implemented, and it may be necessary to make certain modifications according to the actual situation during the practice, for example, if you write an online editor and preview page for markdown, you need to update the scale value in real time according to the height of the input content. However, the main part has been completed, and minor modifications are not difficult.

What is described in this article is not only for the follow-up scroll effect of two scroll container elements, but can also be expanded to the follow-up scroll effect between more elements, which can be realized according to the idea of this article. This article is only specific to two elements for the sake of convenience of explanation.

Summary

The above-mentioned is the native JS control of multiple scroll bars following the scroll effect introduced by the editor to everyone, hoping it will be helpful to everyone. If you have any questions, please leave a message, and the editor will reply to everyone in time. Thank you very much for everyone's support for the yells tutorial!

Statement: The content of this article is from the Internet, and the copyright belongs to the original author. The content is contributed and uploaded by Internet users spontaneously. This website does not own the copyright, has not been manually edited, and does not assume any relevant legal responsibility. If you find any content suspected of copyright infringement, please send an email to: notice#oldtoolbag.com (when sending an email, please replace # with @ to report abuse, and provide relevant evidence. Once verified, this site will immediately delete the content suspected of infringement.)

You May Also Like