Bros! Don’t forget this when using focus and blur events!

Foreword

Welcome to follow the public account of the same name “Panda's Cat“. Articles will be updated simultaneously, and you can also quickly join the front-end communication group!

Recently, my friends have encountered another need, which is to add a border style to the check box when it is selected, which is roughly as follows:

Original effect:

image.png

What you need now:

image.png

The hiding time of this outer border is:

  • Checkbox unchecked
  • Click outside the checkbox content

06CE110E.gif

The friends quickly completed the requirements, but the test students reported the following BUG:

  • Normal speed click

    1.gif

  • Quickly click back and forth

    2.gif

It is obvious that the outer border does not show/hide as expected when clicked quickly. In order to better reproduce the problem, we will re-implement the function and then solve the problem. .

08671C34.jpg

Implement checkbox component

Since this checkbox uses internal business components and is implemented through JSX syntax, the template template syntax is not used here.

Core analysis

The content is relatively simple. To implement a checkbox yourself, the core is nothing more than the following points.

Use to associate

  • In order to achieve the effect of clicking text part directly and clicking checkbox, you can use tags, and the association methods are divided into two types
    • The id of is consistent with the for attribute of < pre>
    • Directly wrap
       <label>
            Click me
            <input type="text" />
        </label>
      

Customize checkbox style

In order to unify the display style, the span element will be used to replace the original checkbox, and the Original checkbox is hidden, such as the checkbox in Ant Design, Element UI.

image.png

Supports v-model two-way binding

In order to facilitate external use, self-encapsulated components need to support the form of v-model two-way data binding, and within the component only the corresponding props and emis need to be Just define the event, as follows:

export default defineComponent({<!-- -->
    props: {<!-- -->
        modelValue: {<!-- -->
            type: Boolean,
            default: false
        }
    },
    emits: ["update:modelValue"],
    ...
})

Effect display

The mouse click position is not displayed in the following effect. The actual click position includes:

  • checkbox itself displays the selected style, including outer border and fill style
  • checkbox text part, hide outer border
  • Area outside checkbox, hide outer border

3.gif

The code is roughly as follows:

// ChcekBox.tsx
import {<!-- --> defineComponent, ref } from 'vue'

export default defineComponent({<!-- -->
    name: 'ChcekBox',
    props: {<!-- -->
        modelValue: {<!-- -->
            type: Boolean,
            default: false
        }
    },
    emits: ["update:modelValue"],
    setup(props, {<!-- --> slots, emit }) {<!-- -->
        const blur = ref(false);

        const onChange = (e) => {<!-- -->
            emit('update:modelValue', e.target.checked)
        }

        const onFocus = () => {<!-- -->
            blur.value = false;
        }

        const onBlur = () => {<!-- -->
            blur.value = true;
        }

        return () => (
            <label class="checkbox-wrapper">
                <span class={<!-- -->["checkbox", props.modelValue & amp; & amp; !blur.value & amp; & amp; "checkbox-checked"]}>
                    <input
                        class="checkbox-input"
                        type="checkbox"
                        name="checkbox"
                        checked={<!-- -->props.modelValue}
                        onBlur={<!-- -->onBlur}
                        onFocus={<!-- -->onFocus}
                        onChange={<!-- -->onChange}
                    />
                    <span class={<!-- -->["checkbox-inner", props.modelValue & amp; & amp; "checkbox-inner-checked"]}></span>
                </span>
                <span class="checkbox-label">{<!-- -->slots.default ? slots.default() : ''}</span>
            </label>
        )
    }
});

// ChcekBox.less
.checkbox-wrapper {<!-- -->
  display: flex;
  align-items: center;
  cursor: pointer;

  .abs {<!-- -->
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    height: 16px;
    width: 16px;
  }

  .checkbox {<!-- -->
    position: relative;
    margin-right: 5px;
    height: 16px;
    width: 16px;
    padding: 10px;
    border-radius: 4px;
    border: 1.5px solid transparent;

     & amp;-checked{<!-- -->
        border-color: #1677ff;
    }

     & amp;-inner {<!-- -->
      .abs();
      border: 1px solid #1677ff;
      border-radius: 4px;

       & amp;-checked {<!-- -->
        background-color: #1677ff;
        border-color: #1677ff;
      }

       & amp;::after {<!-- -->
        box-sizing: border-box;
        position: absolute;
        top: 50%;
        inset-inline-start: 21.5%;
        display: table;
        width: 6px;
        height: 9px;
        border: 2px solid #fff;
        border-top: 0;
        border-inline-start: 0;
        content: " ";
        transform: rotate(45deg) scale(1) translate(-50%, -50%);
        transition: all 0.2s cubic-bezier(0.12, 0.4, 0.29, 1.46) 0.1s;
      }
    }

     & amp;-input {<!-- -->
      .abs();
      opacity: 0;
    }
  }
}

Analyze/solve outer border display BUG

Let’s review the display effect when quickly click back and forth:

2.gif

Then looking back at the code implemented above, it is not difficult to find that the onFocus and onBlur events on checkbox are wanted to be Control the show/hide of the outer border when triggered.

So now there is a problem with show/hide, which is obviously related to the onFocus and onBlur events.

focus and blur events

The focus event is triggered when the element gets focus. This event cannot be canceled and will not bubble .

The blur event is triggered when an element loses focus. This event cannot be canceled and will not Bubbling.

Another point that is most easily overlooked is that they must be triggered from another state to current state, for example:

  • When it is in the focused state and continues to perform the focus action, the focus event will not be triggered.
  • When it is in the out-of-focus state and continues to perform the out-of-focus action, the blur event will not be triggered.

Analyze the problem

First of all, in terms of layout, element and element both use absolute positioning, and the latter will cover the former.

Event bubbling

So our current click operation actually directly operates on the element, so why can it still be triggered on the element What about the bound focus and blur events?

This is not difficult, because there is event bubbling, so the click operation can be passed to the underlying element, naturally The corresponding event can be triggered.

focus and blur are not triggered

When performing a quick click, you will find that neither the focus nor the blur events are triggered, as follows:

5.gif

It is obvious that there is a problem with the display because these two events are not executed.

071C7D88.gif

So the question is why these two events were not executed?

If the last time the blur event is triggered in terms of state changes, then it is understandable that subsequent blur events will not be triggered. After all, the state has not been changed, but focusEvent is not executed, which is not what it should be.

There is no relevant information on this issue yet. Anyone who knows about it can share their insights in the comment area.

Solving problems

Once you know the reason, it is easy to make adjustments. Although focus and blur cannot be triggered all the time, the change event is not affected. as follows:

6.gif

Therefore, you only need to migrate the logic related to showing/hiding the outer border to onChange, as follows:

  • In order to ensure that the onBlur event can be executed normally, when onChange is triggered we should pass e.target.focus() triggers the outer border display logic in order to change the checkbox Focus/Out of Focus status, as mentioned before, only the difference between Current Status and Next Status can trigger the corresponding event.

Since the onChange event is not affected, why do we need onBlur and onFocus?

This is also a question raised by friends in the comment area. In order to avoid everyone having this question, I will explain it here.

Let’s look back briefly at the requirements:

  • Click on the checkbox itself to display the selected styles, including outer border and fill style
  • Click outside the checkbox to hide the outer border

Then we know that when the checkbox itself is clicked, the onChange event will always be triggered. From this perspective, there is really no need for onBlur and onFocus.

But it is worth noting that when you need to click the area outside the checkbox to hide the outer border, the onChange event cannot be executed because at this time checkbox > The selected state has not changed, and this operation is very suitable for the triggering time of the onBlur event, so we need the onBlur event.

Because the onBlur event is needed, but because of the problem mentioned earlier that the onFocus and onBlur events are not triggered when quickly clicked, we need to use onChange< /strong> Manually trigger the onFocus event of checkbox. Only in this way can it be triggered when we click on the area outside the checkbox >onBlur event.

import {<!-- --> defineComponent, ref } from 'vue'

export default defineComponent({<!-- -->
    name: 'ChcekBox',
    props: {<!-- -->
        modelValue: {<!-- -->
            type: Boolean,
            default: false
        }
    },
    emits: ["update:modelValue"],
    setup(props, {<!-- --> slots, emit }) {<!-- -->
        const blur = ref(props.modelValue);

        const onChange = (e) => {<!-- -->
            e.target.focus();
            emit('update:modelValue', e.target.checked)
        }

        const onFocus = () => {<!-- -->
            blur.value = false;
        }

        const onBlur = () => {<!-- -->
            blur.value = true;
        }

        return () => (
            <label class="checkbox-wrapper">
                <span class={<!-- -->["checkbox", props.modelValue & amp; & amp; !blur.value & amp; & amp; "checkbox-checked"]}>
                    <input
                        class="checkbox-input"
                        type="checkbox"
                        name="checkbox"
                        checked={<!-- -->props.modelValue}
                        onBlur={<!-- -->onBlur}
                        onFocus={<!-- -->onFocus}
                        onChange={<!-- -->onChange}
                    />
                    <span class={<!-- -->["checkbox-inner", props.modelValue & amp; & amp; "checkbox-inner-checked"]} onClick={<!-- -->()= >console.log('click in checkbox-inner')}></span>
                </span>
                <span class="checkbox-label">{<!-- -->slots.default ? slots.default() : ''}</span>
            </label>
        )
    }
})

The final effect is as follows:

7.gif

Finally

Welcome to follow the public account of the same name “Panda's Cat“. Articles will be updated simultaneously, and you can also quickly join the front-end communication group!

To sum up, when you need to use the focus and blur events in your project to achieve related requirements, you should pay special attention, especially in the safari browser or IOS devices etc. There may be cases where the focus and blur events do not take effect.

Hope this article is helpful! ! !

074F3CCD.jpg