preface

In recent projects, there are often problems caused by users’ multiple clicks, and the use of anti-shake/throttling is quite frequent. Therefore, WE want to achieve this through the way of vUE custom instruction. If you need to use anti-shake in the back, just use the command.

The original code

<button @click="sayHello">submit</button>
Copy the code
sayHello() {
    console.log('Hello! ')}Copy the code

What I want to achieve:

<button v-throttle="200" @click="sayHello">submit</button>
Copy the code

With this setting, the submit button can be executed only once for multiple clicks in 200ms and as soon as it is clicked. If you do not set time (200), the system runs only once within 2000ms by default.

Anti – shake/throttling differences and options

<1> Throttling means that the JS method only runs once in a certain period of time. For example, the human eye blinks once in a certain period of time. This is the function. <2> Anti – shake refers to the case of frequent triggering, only enough idle time, to execute the code once. Take a bus in life, for example, is a certain period of time, if someone has to swipe the card to get on the bus, the driver will not drive. A driver only drives when someone else’s card is gone.

(1) Differences In my opinion, their differences lie in the application scenarios, for example, as follows:

[Application Scenario] : Click continuously in 0.2s,0.4s,0.6s,0.8s respectively to trigger searchAPI.

[Effect after shaking] : 0.8s after the real send searchAPI;

[Throttling effect] : at 0.2s, the searchAPI interface is triggered immediately when someone clicks, and within x seconds, the user clicks are invalid.

Stole a GIF from the InternetVUE anti – shake and throttling the best solution – functional components), the feeling looks clearer:(2) Choice

On my side, I need to respond immediately when the user clicks on it. The clicks in the next few seconds are invalid because of throttling effect, so I choose throttling.

How do I create custom directives

From the Vue official website, the custom instruction tutorial (poke here can see the details), find the following two points for me to use:

(1) Custom directive hook functions: BIND, Inserted, Update, componentUpdated, unbind

I use bind, which only needs to be initialized once.

(2) Hook function parameters

El: DOM manipulation can be done directly (for example, el.addeventListener, el.onclick).

Binding: The directive binding value can be obtained by value.

Consider: How can I add a listening click event without interfering with the original click event

If there are more than one handler for the onclick event, the latter overrides the former. AddEventListener Registers multiple listeners for an event without overwriting it.

Use addEventListener of course (IE will use attachEvent, however I only need to support Chrome in my project)

The first option

Implementation approach

How do I control whether the sayHello() method executes? The first thing THAT comes to mind is to use a variable called isDisableClick to control the sayHello() method by registering the click event to store the variable isDisableClick in the DOM.

Define anti-shake instructions:

Vue.directive('throttle', {
  bind: function (el, binding) {
    let throttleTime = binding.value // Throttle time
    if(! throttleTime) {// If the user does not set the throttling time, the default is 2s
      throttleTime = 2000
    }
    let cbFun
    el.addEventListener('click'.() = > {
      el.isDisableClick = true
      if(! cbFun) { cbFun =setTimeout(() = > {
          el.isDisableClick = false
          cbFun = null
        }, throttleTime)
      }
    }, true)}})Copy the code

Use instructions:

<button @click="sayHello" ref="target" v-throttle>submit</button>
Copy the code
sayHello() {
  if (!this.$refs.target.isDisableClick) {
    console.log('Hello! ')}}Copy the code

Question:

Use to write redundant code, feel the use of unpleasant, discard!

The second option (seen online)

Implementation approach

Prevent the onclick event from firing with the disabled attribute

Define anti-shake instructions:

Vue.directive('throttle', {
  bind: function (el, binding) {
    let throttleTime = binding.value // Throttle time
    if(! throttleTime) {// If the user does not set the throttling time, the default is 2s
      throttleTime = 2000
    }
    let cbFun
    el.addEventListener('click'.() = > {
      if(! el.disabled) { el.disabled =true
                cbFun = setTimeout(() = > {
                    el.disabled = false
                    cbFun = null
                }, throttleTime)
            }
    }, true)}})Copy the code

Use instructions:

<button @click="sayHello" v-throttle>submit</button>
Copy the code
sayHello() {
    console.log('Hello! ')}Copy the code

Question 1:

For buttons, you can use the Disabled attribute to keep them from firing, but some buttons are implemented with div/span, etc. The disabled attribute does not prevent div onclick events.

Solution to problem 1:

Set style pointer-events: None to block div/span onclick events.

Question 2:

Pointer-events: None makes the element entities “dashed”. For example, a button element with pointer-events: None applied, then the button we see on the page is just an illusory shadow, which you can interpret as a mirage, the body of a ghost. When we touch it with our hands we can easily pass through it without feeling anything.

Excerpt from Zhang XinxuCSS3 pointer-events: None Example and extension

Since pointer-events: None means that the element no longer exists, will a click in this case pass through to its outer layer?

If you set pointer-events: None, clicking on the child node is invalid, but clicking on its parent node is equivalent to clicking on its parent node.

Third option

Define anti-shake instructions:

Vue.directive('throttle', {
  bind: (el, binding) = > {
    let throttleTime = binding.value; // Anti-shake time
    if(! throttleTime) {// If you do not set the anti - shake time, the default value is 2s
      throttleTime = 2000;
    }
    let cbFun;
    el.addEventListener('click'.event= > {
      if(! cbFun) {// This is the first execution
        cbFun = setTimeout(() = > {
          cbFun = null;
        }, throttleTime);
      } else{ event && event.stopImmediatePropagation(); }},true); }});Copy the code

Use instructions:

<button @click="sayHello" v-throttle>submit</button>
Copy the code
sayHello() {
    console.log('Hello! ')}Copy the code

PS: a small white front, welcome to leave a message error, thank you ~