preface

Scrolling up and down is one of the common interactions on a page, in order to keep some important information on the interface and not lose sight of it as the page scrolls. The front end pins it to the top before it scrolls up off the screen and releases it when it scrolls down to where it should appear. This interaction is called top sucking. For example, the banner title of the page, some important shopping scene filtering criteria.

Scenario use case

Sometimes a generic component is written, and if it has the ability to suck the top, the location of the suck top must be determined from the design draft when first developed. Our front-end components are oriented towards build scenarios and can be freely combined by users to build pages. You can’t be sure if the user has added other top components in front of it (maybe the user doesn’t even know it has the top capability, but just looks good and uses it), and the top positions of multiple components will likely clash when scrolling. The most common thing is everybody grabs top:0, and they’re all on top of each other. Of course, the user does not want to adjust the collocation and layout of the page in order to absorb the top, but it should be adsorbed in the appropriate position for normal people.

solution

The core is to resolve conflicts between multiple ceiling components, so manage them. Register with the top queue in a common way, and determine the top relationship according to the order in which components are placed in the DOM. The rolling behavior is the engine that causes the top-suction interaction. After the rolling behavior occurs, the top-suction queue is polled to judge whether the components are top-suction or not. As long as the component is less than or equal to zero from the bottom boundary of the previous top component (almost overlapping), it is time to top. The top suction time of the first top suction assembly is less than or equal to 0 from the position of the viewport.

API design

  • Simple enough, a single line of code declaration can make the target component top-sucking.
  • Components are marshalled in the natural order in the DOM, not in the order in which they are declared.
  • The page does not shake naturally before and after the top is lifted.
  • The style after the top can be controlled. If the level behind the top is not high enough, higher level components without the top will cover the component while rolling. By the way, you can also achieve white suction top, suction top red.
  • Default provides the logic of the top judgment, developers can rewrite. The rewritable method automatically injects the top queue, the various states of its own elements, and the position relationship with previous top elements.

A declarative way

Class inheritance

By default, a Class is exported, and components that inherit the Class directly have the ability to suck top. If the component’s Render method returns multiple root elements, and only the first root element can top, it is best to return a single root node.

import Sticky from '@ali/rox-sticky-helper';
export default Class extends Sticky {
	render() {
		return. }}Copy the code

Components use

Provide a StickyView component, and its children will have top sucking capability. StickyView doesn’t wrap any elements around its child components. Similarly, if there are multiple direct child components, only the first direct child can top, preferably only one direct child.

import { StickyView } from '@ali/rox-sticky-helper';
export default function() {
	return (
	  <StickyView>
	    <! -- Subcomponent -->
	  </StickyView>
	);
}
Copy the code

Hooks

React16 and RAx1. x both provide hooks. Here also provides a createStickyRef hook that acts like the React createRef hook, increasing the ability to suck components to the top.

import { createStickyRef } from '@ali/rox-sticky-helper';
const stickyRef = createStickyRef();
export default function() {
	return (
	  <div ref={stickyRef}>
	  </div>
	);
}
Copy the code

Top arrangement sequence

Normally, components are displayed on the screen in the sequence of A, B, and C, and should be adsorbed on the screen in the same sequence during top suction. Therefore, each time a top component is declared, it is placed in the appropriate position in the top queue based on its position in the HTML layout.

Use the standard DOM APIcompareDocumentPosition (), can easily determine the location of the relationship between two DOM. IE9 already supports it.

List of Returned results

Code sample

<html>
  <head></head>
  <body></body>
</html>

// case1: Get the position relationship between the head and body elements
document.head.compareDocumentPosition( document.body )
// Return 4, which conforms to rule 3 in the table above

/ / in turn
document.body.compareDocumentPosition( document.head )
// Return a value of 2, which conforms to rule 2 in the table above

// case2: Obtain the position relationship between document and body
document.compareDocumentPosition( document.body )
// The result is 20
// Body is included by document, return 16, in line with rule 2 in the table above
// Since document contains body, document must precede body, and return 4, in accordance with rule 3 in the table above
// 16 + 4 = 20

/ / in turn
document.body.compareDocumentPosition( document )
// The result is 10
// document contains body, returns 8, complies with rule 4 in the table above
// Document before body, return 2, in line with rule 2 in the table above
// 8 + 2 = 10

Copy the code

The placeholder suction a top

The way to suction a top

Css3 added the position: sticky attribute to allow elements to remain unchanged in normal cases and remain fixed after scrolling to the specified top. However, the sticky attribute is affected by the height of the parent element, overflow, and will fail in the case of flexible structure. Finally, we decided to use a simple and crude position: Fixed to shield the influence of all hierarchical structures.

Highly placeholder

An element that would normally be in the document flow, assuming the height is 50px. After the top, the position will change to fixed, which is out of the normal document flow. The original position will be reduced by 50px, and the element behind it will jump 50px upward. Similarly, if you scroll down the page and the element is removed from the top and returned to the normal document flow, you will immediately have an extra 50px to push the following element down. To prevent page jitter, insert an invisible placeholder of the same height when the top is lifted and then removed.

Remove top absorption

The component pulls out of the document flow, leaving a placeholder element inserted in place. Conversely, when the placeholder element is about to completely move away from the previous top element boundary, it is time to remove the top element. Restore the styling of the original component and remove the placeholder element from the DOM.

Suction style of top

When a component is in the flow of a document, the width and height can be adaptive to the layout of the parent element, and the background color can be directly reflected by the parent element.

No style suction top

Have style suction top

Code example – Inheritance

import Sticky from '@ali/rox-sticky-helper';
export default class extends Sticky {
  getStickyStyle() {
    return { color: '#fff'.backgroundColor: '#2990dc' };
  }
  render() {
    return (
      <div id="d1" style={{ width: 750.lineHeight: 100.backgroundColor: '#fff' }}>Top suction module 1</div>); }}Copy the code

Code example – Component

import { StickyView } from '@ali/rox-sticky-helper';
function Module2() {
  return <StickyView
    getStickyStyle={()= > ({ color: '#fff', backgroundColor: '#f15a4a' })}
  >
    <div id="d2" style={{ width: 750.lineHeight: 100.backgroundColor: '#fff' }}>Top suction module 2</div>
  </StickyView>;
}
Copy the code

Code example -Hooks

import { createStickyRef } from '@ali/rox-sticky-helper';
const ref = createStickyRef({
  getStickyStyle() {
	return { color: '#fff'.backgroundColor: '#f39826'}; }});function Module3() {
  return (
    <div ref={ref}
      style={{ width: 750.lineHeight: 100.backgroundColor: '#fff'}} >Top suction module hook</div>
  );
}
Copy the code

Control the timing of top suction

Here again to repeat the suction top control time bar!!

The API automatically listens for scrolling events on the page, and by default, the first element is less than or equal to zero away from the viewport to determine the top. The bottom boundary between the non-first element and the previous element is less than or equal to 0, and the top is judged.

If you have a weird business logic that needs to be timed to peak, override the onStickyScroll method.

Code sample

import Sticky from '@ali/rox-sticky-helper';
class Module1 extends Sticky {
  onStickyScroll(helper) {
    // Default processing logic
    const {
      prevTopDiff, // The distance between the top assembly and the previous one
      prevBottom, // The bottom boundary of the previous ceiling assembly is at the viewport position
      self, // The top of the queue
      stickyAPI, // Top related API
      forceReflow
    } = helper;
    
    const {
      dom, // The component's DOM element
      isSticky // Whether to suck the top
    } = self;

    if (isSticky) {
      if(! self.keepSticky) {// Whether it is time to lift the ceiling
        stickyAPI.resetSticky(self);
      } else if(dom.getBoundingClientRect().top ! = prevBottom) {// After the suction top position deviation occurs, conduct secondary calibration
        // Sometimes the height of the previous element changes as the top is lifted, and the following element needs to change the top position
        dom.style.top = `${prevBottom}px`; }}else if (prevTopDiff <= 0) { // The distance from the top element is less than or equal to 0
      stickyAPI.setSticky(self, { // Call the top setup API
        position: 'fixed'.zIndex: 100.top: `${prevBottom}px`. self.ref.getStickyStyle(helper),// Get a user-defined top suction style}, forceReflow); }}}Copy the code

Initialize immediately suction top

The first time the page scrolls up, the component follows the page up by at least 3 pixels. At this point, the top of the component from the viewport is -3px, and it is time to top. The component needs to be moved from -3px to 0px, and the navigation component will shake up and down on the page.

Thus, by default, a top determination is automatically performed after the component is declared top. In this way, components at the top of the screen can be initialized immediately after the top, no scrolling jitter. If you have a safe distance between your component and the top of the page, you can manually turn off the automatic top sucking.

import Sticky, { StickyView, createStickyRef } from '@ali/rox-sticky-helper';

// Inheritance mode
class Module1 extends Sticky {
  // The default value is true.
  autoStickyTrigger = false;
}

// Component mode
<StickyView autoStickyTrigger={false} / >// Hooks
createStickyRef({ autoStickyTrigger: false })
Copy the code

Top derived interaction cases

Multi – line navigation, top synthesis line

2 lines of screening conditions, after the top change a line, left and right horizontal slide