current position:Home>H5 realize more effects of popwindow drop-down

H5 realize more effects of popwindow drop-down

2022-01-29 15:24:15 qyjandroid

Little knowledge , Great challenge ! This article is participating in “ A programmer must have a little knowledge ” Creative activities .

This article has participated in  「 Digging force Star Program 」 , Win a creative gift bag , Challenge creation incentive fund .

Preface

Hello Hello everyone ! I'm the front end unknown

background

Do the mobile end H5 In business, we often encounter more buttons in the upper right corner , Click the more button , The drop-down menu appears , Clicking on other areas of the menu not only closes the pop-up window, but also triggers the event of clicking on the area .

Here's the picture :

image.png

Click on the red box area , Pop up window will disappear , If you click on “ Open another pop-up window ” On the button , Also trigger the click event of this button .

difficulty

Because of this prompt, the pop-up window is generally positioned relative to the clicked element , So I usually write React Components like to hang a sub directly under the button div Elements , Directly control the explicit and implicit state to display the pop-up window . How to close the pop-up window by clicking on the area outside the pop-up window element ??? This is a tangled problem .

Scheme 1

Let's make the pop-up area bigger , Make it the same as the width and height of the screen , Because you want to click on other areas, click events in other areas will be triggered , Make the pop-up window mask transparent , Then set the mask css "pointer-enents:none", The content display area is set to click without penetration , But it's not easy to capture the event of clicking on other areas , Only passively by css Trigger penetration event . The implementation should be able to achieve , High complexity , There are many shortcomings .

Option two

Click the more button , Popup ( Content area size only ) On display , stay body Register click event on , Cancel the registration event when the pop-up window is closed . Because clicking will have event.target coming , Let's distinguish event.target Whether it is an element in the pop-up area , If it's a non pop-up area element , Let's just close the pop-up window .

effect

QQ Recording screen 20211013111210 00_00_00-00_00_30.gif

Component code

Detailed comments are added to the code , You can refer to it .

DropDownMenu.tsx

import React from 'react';
import BaseComponent from '../BaseComponent';
import { isContains, addEventListenerWrap } from './popUtils';
import './index.scss';
import Button from '../Button';

interface Props {
  /** *  Button  * @memberOf Props */
  renderBtnView: () => JSX.Element;

  /** * * *  Pop-up content  * @memberOf Props */
  renderPopContentView: () => JSX.Element;

  /** * *  Pop up window root node range  * @memberOf Props */
  getRootDomNode?: () => HTMLElement;

  /** *  style  * @type {string} * @memberOf Props */
  className?: string;
}

interface State {
  /** * *  Whether or not shown pop * @type {boolean} * @memberOf State */
  showPop: boolean;
}

/** * *  more - Drop down menu pop-up  * @export * @class TipPop * @extends {BaseComponent<Props, State>} */
export default class DropDownMenu extends BaseComponent<Props, State> {
  private domListener = null;

  private popupRef = null;

  constructor(props) {
    super(props);
    this.state = {
      showPop: false,
    };
    this.popupRef = React.createRef();
  }

  componentDidUpdate(_privProps, prevState) {
    if (!prevState.showPop && this.state.showPop) {
      // Pop up status changes , From hide to show , Add listener 
      this.setListener();
    } else if (prevState.showPop && !this.state.showPop) {
      //// Pop up status changes , From hide to show , Cancel listener 
      this.cancelListener();
    }
  }

  /** * * *  Set listening  * @memberOf DropDownMenu */
  setListener = () => {
    // Get root node 
    const rootDom = this.getRootDomNode();
    // The default is to cancel listening once 
    this.cancelListener();
    this.domListener = addEventListenerWrap(rootDom, 'click', event => {
      const { target } = event;
      const root = this.getRootDomNode();
      const popupNode = this.getPopupDomNode();
      // The judgment is the click event in the root node box , And it's not a pop-up area , For any click, the area disappears .
      if (isContains(root, target) && !isContains(popupNode, target)) {
        console.log(' Direct closure ===', target, isContains(popupNode, target));
        // Direct closure 
        this.hidePop();
      }
    });
  };

  /** * * *  Cancel monitoring  * @memberOf DropDownMenu */
  cancelListener = () => {
    if (this.domListener) {
      this.domListener?.remove();
      this.domListener = null;
    }
  };

  /** * *  obtain pop Pop up node  * @returns * * @memberOf DropDownMenu */
  getPopupDomNode() {
    return this.popupRef.current || null;
  }

  /** * * *  Get the default root node  * @memberOf DropDownMenu */
  getRootDomNode = (): HTMLElement => {
    const { getRootDomNode } = this.props;
    if (getRootDomNode) {
      return getRootDomNode();
    }
    return window.document.body;
  };

  /** * * *  Show pop ups  * @memberOf DropDownMenu */
  showPop = () => {
    const { showPop } = this.state;
    console.log(' Click on ===', showPop);
    // The pop-up window here opens , Click the button again , You can close the pop-up window 
    if (showPop) {
      this.setState({
        showPop: false,
      });
      return;
    }

    this.setState({
      showPop: true,
    });
  };

  /** * * *  Hide the pop-up window  * @memberOf DropDownMenu */
  hidePop = () => {
    this.setState({
      showPop: false,
    });
  };

  render() {
    const { className } = this.props;
    const { showPop } = this.state;
    return (
      <div className={`tip-pop ${className}`} ref={this.popupRef}> <Button className="tip-pop-btn" onClick={this.showPop}> {this.props.renderBtnView()} </Button> {showPop ? ( <div className="tip_pop-content">{this.props.renderPopContentView()}</div> ) : null} </div>
    );
  }
}


 Copy code 

popUtils.ts

/** * *  Judge whether it includes  * @export * @param {(Node | null | undefined)} root * @param {Node} [n] * @returns */
export function isContains(root: Node | null | undefined, n?: Node) {
  if (!root) {
    return false;
  }

  return root.contains(n);
}

/** * *  Add listening  * @export * @param {any} target * @param {any} eventType * @param {any} cb * @param {any} [option] * @returns */
export function addEventListenerWrap(target, eventType, cb, option?) {
  if (target.addEventListener) {
    target.addEventListener(eventType, cb, option);
  }
  return {
    remove: () => {
      if (target.removeEventListener) {
        target.removeEventListener(eventType, cb);
      }
    },
  };
}

 Copy code 

call


   renderBtnView = () => (
    <div className="more-btn-style"> <div className="btn-dot-1" /> <div className="btn-dot-1" /> <div className="btn-dot-1" /> </div>
  );

  renderPopContentView = () => (
    <> <Button className="rule-btn"> The rules </Button> <Button className="history-btn"> Record </Button> </>
  );

  render() {
    console.log('this.props==', this.props);
    return (
      <div className="home"> <div className="home-title"> test </div> <DropDownMenu className="more-btn" renderBtnView={this.renderBtnView} renderPopContentView={this.renderPopContentView} /> <Button className="other-btn" onClick={() => { window.alert(' Trigger '); }} >  Open another pop-up window  </Button> </div>
    );
  }
 Copy code 

Afterword

You are welcome to make more comments . This article mainly records the more interesting requirements and solutions of daily work , I'll see you in the comments section of the scheme , Praise once ! Welcome comments .

copyright notice
author[qyjandroid],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2022/01/202201291524121379.html

Random recommended