Analysis of scroll properties | JD Cloud technical team

Recently, there is a project that needs to use js natively to develop sliding components, and various attributes of dom elements are frequently used, among which various types of height and top attributes are mostly, with similar names, and the meanings are easy to confuse. Therefore, I specially summarized the commonly used knowledge points. At the end of the article, we will challenge to implement a simple mobile Scroll component.

To understand height and top, we must start with the box model. First, let's get acquainted with the box model defined in css3. The actual area occupied by dom elements on the page can be illustrated by the following picture:

 We can think of it as an egg, from the outside to the inside are

Orange area - outer margin margin: egg shell

Black area - border border: eggshell membrane

Green area - inner margin padding: albumen

White area - content content: egg yolk

The part we really care about is the content, which is the most nutritious part of the egg. With so many layers wrapped around the content, we will feel the overall density of the DOM structure in the page, and will not be troubled by the dense text and pictures. In order to better describe the box model, some other interfaces are defined in the web specification to describe them, which are the protagonists we will talk about today:

 

In the picture above, we describe two layers of dom elements with a nested relationship. Note that the content area here is different from the previous picture. The content area here is also an independent dom element (that is, it also has its own margin and border. , padding and content!, for the sake of simplification, the child elements are no longer specifically drawn), because the overall height of the child elements is very high, even exceeding the height of the parent element, it cannot be fully displayed in the parent element (the invisible part beyond that is used Indicated in gray), that is, the two constitute a rolling relationship. Let's try to describe the following properties:

一、clientHeight

Read-only property. clientHeight is actually the height of the vertical scroll bar. In general, the vertical scroll bar should be close to the upper and lower borders, so clientHeight = upper and lower padding + content height. Don’t forget that there is a special case. When there is a horizontal scroll bar, you also need to consider that the horizontal scroll bar occupies part of the space of the vertical scroll bar, that is: clientHeight = up and down padding + content height - horizontal scroll bar height. If you use an egg as a metaphor, clientHeight is a shelled egg.

Two, clientTop

Read-only property. Describes the width of the top border. The top border is easy to remind us of the thickness of the hair. Next time in front of the mirror, you can say to yourself: Your clientTop has decreased, but you have also become stronger.

3. offsetHeight

Read-only property. offsetHeight is similar to clientHeight. Observation shows that there is an extra layer of border than clientHeight: offsetHeight = clientHeight + upper and lower borders. Now we go back to the moment before peeling the eggs, and the eggs are taken out of the water and washed and presented-offsetHeight.

Four, offsetTop

Read-only property. It returns the distance of the current element relative to the top padding of its offsetParent element. This distance does not include the border width of itself and the parent element, in fact: offsetTop = its own upper margin + parent element's upper padding, which is equivalent to the thickness of the baffle between the egg and the egg box.

Five, scrollHeight

Read-only property. Describes a measure of the height of an element's content, including content that is not visible in view due to overflow. We can hack it to display the invisible content in the parent element. The child element can be divided into two parts in the vertical direction: the outer margin and the inner offsetHeight:

For parent-child elements with a scrolling relationship, their scrollHeight has different meanings:

(1) For child elements, since the child element itself does not contain the overflow part, its scrollHeight and clientHeight have the same value

(2) For the parent element, since the content of the parent element is actually "supported" by the child element, its scrollHeight is the actual height after "expanding" the content area: parent element scrollHeight = offsetHeight of the child element + child element Up and down margin + own up and down padding

Six, scrollTop

Read-write property, you can get or set the number of pixels the content of an element scrolls vertically. This is the first property we have encountered so far that can support settings.

(1) In the initial state, the content is not scrolled vertically, and its value is 0

(2) When the content scrolls to the bottom in the vertical direction, since the actual height of the content area is scrollHeight, the displayable height is clientHeight, and the extra part is that the scrollTop value at this time is scrollHeight - clientHeight

So it can be concluded that: 0 <= scrollTop <= scrollHeight - clientHeight

Seven, actual combat

After learning the above attributes and information, we imitate the scroll-view component function of the JD applet to implement a common function of the H5 version of the sliding component: pull-down refresh and pull-up loading of the list. To achieve this function, it can be roughly divided into three core points:

(1) Scrollable: There needs to be a non-movable shell and a slidable content area.

(2) Gesture recognition: Through the touch attribute of the mobile terminal, we can compare the finger positions of touchend and touchstart to perform gesture recognition simply.

(3) Event triggering: According to the critical condition of the sliding position, it is judged whether refresh and loading events should be triggered.

Part of the implementation code is as follows:

// scroller.js

export default class Scroller {
	constructor(el, option) {
		this._el = el
		this._parent = el.parentNode
		this._option = option
		this._pos = 0
		this._handleScrollStart = this.handleScrollStart.bind(this)
		this._handleScroll = this.handleScroll.bind(this)
		this.init()
	}
	init() {
		this._el.addEventListener('touchstart', this._handleScrollStart)
		this._el.addEventListener('touchend', this._handleScroll)
		if(this._option.auto) {
			this.handleRefresh()
		}
	}
	destroy() {
		this._el.removeEventListener('touchstart', this._handleScrollStart)
		this._el.removeEventListener('touchend', this._handleScroll)
	}
	handleScrollStart(e) {
		const touch = e.targetTouches[0] || e.changedTouches[0]
		this._pos = touch.clientY
	}
	handleScroll(e) {
		const touch = e.targetTouches[0] || e.changedTouches[0]
		const delta = touch.clientY - this._pos
		if(delta >= this._option.threhold) {
			// 手势向下,且下行滚动距离超过判定阈值
			if(this._parent.scrollTop == 0) {
				this.handleRefresh(e)
			}
		} else if(this._parent.scrollHeight > this._parent.clientHeight && delta <= -this._option.threhold){
			// 内容可滚动,且上行滚动距离超过判定阈值
			if(this._parent.scrollTop > this._parent.scrollHeight - this._parent.clientHeight - this._option.threhold){
				this.handleLoad(e)
			}
		}
	}
	handleRefresh(...params) {
		this._option.onRefresh && this._option.onRefresh.apply(this, params)
	}
	handleLoad(...params) {
		this._option.onRefresh && this._option.onLoad.apply(this, params)
	}
}

Call the demo:

var inner = document.getElementsById('inner')
var list = []
var pageIndex = 1
function getMockData() {
	const newData = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => i+(pageIndex-1)*10)
	setTimeout(() => {
	    if(pageIndex==1) { list = newData } else if(pageIndex < 5) { list.push(...newData) } else { alert('没有更多内容了') }
	    pageIndex++
	    inner.innerHTML = list.map(i => `<div class="inner-item">${i}</div>`).join('')
	}, 300)
}
function onRefresh() {
	pageIndex = 1
	getMockData()
}
new Scroller(inner, {
	threhold: 20,
	onRefresh: onRefresh,
	onLoad: getMockData,
	auto: true
})

You can try it yourself, and the effect is not bad~~~ Welcome to the message area to discuss and exchange.

Author: Jingdong Retail Chen Zhen

Source: JD Cloud Developer Community

 

RustDesk 1.2: Using Flutter to rewrite the desktop version, supporting Wayland's alleged GPT-4 model architecture leak: Contains 1.8 trillion parameters, using a mixed expert model (MoE) Musk announced the establishment of xAI company deepin V23 successfully adapted WSL CentOS project claims " Open to all" Rust 1.71.0 Stable Release React Is it having an Angular.js moment? Microsoft launches a new default font, Aptos, to replace CalibriMicrosoft : Increase efforts to use Rust IntelliJ IDEA 2023.1.4 release on Windows 11
{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/4090830/blog/10089164