vaadin / web-components

A set of high-quality standards based web components for enterprise web applications. Part of Vaadin 20+
https://vaadin.com/docs/latest/components
454 stars 83 forks source link

Touchdevice: NumberField focus stays when step buttons of another field is pressed #7494

Closed jasi-ose closed 3 months ago

jasi-ose commented 3 months ago

Description

I have a problem using input fields on mobile devices with touchscreen. There are multiple NumberFields. Now I focus the first field and change the input when the softkeyboard appeared. I can use the step buttons and everything is fine. But if the softkeyboard is opened and i touch the step buttons of another NumberField, the focus remains in the "old" NumberField with a blinking cursor. The step buttons (of the other field) work correctly but its confusing that the focus stays. If you try it on desktop with clicking an writing by hardware devices you can see that at least the focus gets lost when clicking on the step buttons of another NumberField.

Expected outcome

I would expect the "old" NumberField to lose the focus when the step buttons of another field are pressed.

Minimal reproducible example

https://vaadin.com/docs/latest/components/number-field#step-buttons on mobile devices with touchscreen!

Steps to reproduce

Use a mobile device:

  1. focus a field (the softkeyboard should open)
  2. write a number with the softkeyboard
  3. touch a step button of another NumberField
  4. write a number again -> this behaviour is totally confusing

Environment

Vaadin version(s): 24.3.12 OS: Win10

Browsers

Issue is not browser related

Legioth commented 3 months ago

Note that this is even worse if the focused field is scrolled out of view since it will then scroll back into view when you tap the step buttons of the other field - thus causing the field that you interacted with to scroll away and potentially even out of view.

DiegoCardoso commented 3 months ago

As a workaround, this code can be used:

const numberField = document.querySelectorAll('vaadin-number-field');
    numberField.forEach((field) => {
      field.shadowRoot.querySelector("[part='increase-button']").addEventListener('touchend', () => {
        if (document.activeElement && document.activeElement !== field.inputElement) {
          document.activeElement.blur();
        }
      });
      field.shadowRoot.querySelector("[part='decrease-button']").addEventListener('touchend', () => {
        if (document.activeElement && document.activeElement !== field.inputElement) {
          document.activeElement.blur();
        }
      });
    });

It checks if the active element is not the same as the input element the user is interacting with and forces the active element to be blurred. Nothing happens if the focused element is the same one the user is tapping the increase/decrease buttons.

For Flow users, this can be done via the element API:

numberField.getElement().executeJs("""
       this.shadowRoot.querySelector("[part='increase-button']").addEventListener('touchend', () => {
         if (document.activeElement && document.activeElement !== this.inputElement) {
           document.activeElement.blur();
         }
       });
       this.shadowRoot.querySelector("[part='decrease-button']").addEventListener('touchend', () => {
         if (document.activeElement && document.activeElement !== this.inputElement) {
           document.activeElement.blur();
         }
       });
    """);
jasi-ose commented 3 months ago

Can you give me a solution to apply this workaround to input fields inside a lit renderer in a lazy loading grid? Thats our most common use case.

web-padawan commented 3 months ago

Checked the original implementation and the reason why we don't focus number field on step buttons touch was to prevent opening the virtual keyboard as specified in https://github.com/vaadin/vaadin-text-field/pull/292#pullrequestreview-191120271:

If you use the stepper controls for increasing/decreasing the value, we probably don't want the field to unnecessarily get focused. Focusing the input opens the virtual keyboard on mobile which might be undesired.

Back then, we didn't consider the case of using stepper controls while having other number field focused. So IMO the proper fix would be to incorporate the blur logic into the component itself. I'll look into it.

DiegoCardoso commented 3 months ago

This should work for the case of using it with LitRenderer:

        var touchendListener = """
            e => {
                const isFromStepButtons = e.composedPath().some(el => el.part?.value.includes('increase') || el.part?.value.includes('decrease'));
                if (isFromStepButtons) {
                    if (document.activeElement && document.activeElement !== e.target.inputElement) {
                        document.activeElement.blur();
                    }
                }
            }
            """;
        grid.addColumn(
            LitRenderer.<Number>of("<vaadin-number-field @touchend=${" + touchendListener + "} step-buttons-visible value=${item.value}></vaadin-number-field>")
                .withProperty("value", e -> e));