Autofocus for next field with Javascript + Maxlength

Asked

Viewed 316 times

4

I’m trying to go to the next form field however the INPUTS are inside DIVS and I can’t make it work. It only works without DIVS, I’ve tried several combinations in the script and nothing.

Script:

var container = document.getElementsByClassName("container")[0];
container.onkeyup = function(e) {
    var target = e.srcElement;
    var maxLength = parseInt(target.attributes["maxlength"].value, 10);
    var myLength = target.value.length;
    if (myLength >= maxLength) {
        var next = target;
        while (next = next.nextElementSibling) {
            if (next == null)
                break;
            if (next.tagName.toLowerCase() == "input") {
                next.focus();
                break;
            }
        }
    }
}

Form:

<form class="container">
    <div>
        <input type="text" maxlength="10" />
    </div>                              
    <div>
        <input type="text"  />
    </div>  
</form>

2 answers

6


Just create a list of all inputs that exist in your form. For this, you can use:

const allFields = document.querySelectorAll('form input');

So I would do like this:

const allFields = document.querySelectorAll('form input');

allFields.forEach((field, index) =>
  field.addEventListener('input', (event) => {
    const maxLength = parseInt(event.target.getAttribute('maxlength'), 10);
    const nextField = allFields[index + 1];

    // Três condições farão com que o próximo campo NÃO seja focado:
    // 1. O atributo `maxLength` não estiver definido (se isso ocorrer, a
    //    variável `maxLength` será `NaN`); Ou:
    // 2. O valor do campo tiver um tamanho diferente do `maxLength`; Ou:
    // 3. Não existir um próximo elemento.
    if (
      Number.isNaN(maxLength) ||
      event.target.value.length !== maxLength ||
      !nextField
    ) {
      return;
    }

    nextField.focus();
  })
);
<form>
  <div>
    <input type="text" maxlength="5" />
  </div>
  <div>
    <div>
      <!-- Não há limites para o nível de aninhamento! -->
      <input type="text" maxlength="3" />
    </div>
  </div>
  <div>
    <input type="text" />
  </div>
</form>

I think it ended up being a little simpler too, since we eliminated the use of while to find the next element, emphasizing simply on the "form and its fields". :)

The functioning of script above is quite simple:

  1. We captured all the inputwhich are part of the form (form);
  2. We added a Listener for the event input to each of the camps;
  3. When a field issues the event input, we make the necessary checks (explained in the code);
  4. We find the element by accessing the next input through the iteration index (within the forEach current) added to 1.

Note about the event input

It is important to point out that I changed the event keyup to the input, because I think it is more appropriate in that case, since it is issued only when the value of the field is changed, unlike the keyup, it emits by pressing any key when the element is focused.

There is a gain in the user experience with this exchange, because if one of the fields is already with the character limit, if listening to the event keyup, the script move to the next field even if I was just browsing the text with, for example, the arrow keys on the keyboard, which is not the most friendly behavior.

  • 1

    Great solution, it is clean and worked perfectly, thank you very much.

1

I modified the logic, but maintaining the same principle of navigating the DOM tree.

The code sequentially auto-focus all input[type=text] within a <form>, independent of the hierarchical depth that the input[type=text] can be found inside the <form>.

For when a input[type=text] reach the maximum number of characters, allowed, typed the code starts the search by the next input[type=text] at its hierarchical level. Not finding the code searches through the same hierarchy and consequently seeking the closest occurrence of input[type=text].

Not finding the algorithm passes to a higher hierarchical level and redoes the search for the next input[type=text].

In this case, I increased the complexity of nesting within the form according to the suggestion of the operator Luiz Felipe maxlength for 5 in order to decrease the typing in the test, I put some <div> and some <span>, added a <div> without a respective to test a possible code failure and ended with a <div> out of the form that should not participate in the selection chain.

var container = document.getElementsByClassName("container")[0];
container.onkeyup = function(e) {
    var target = e.srcElement;
    var maxLength = parseInt(target.attributes["maxlength"].value, 10);
    var myLength = target.value.length;
    if (myLength >= maxLength) {
       //Primeiro procura pelo próximo input[type=text] na mesma hierarquia do elemento que disparou o evento
       target = procurarInput(e.srcElement);       
       if (target != null) return target.focus();

       //Depois procura pelo próximo input[type=text] numa hierarquia superior a do elemento que disparou o evento           
       var aux = e.srcElement;
       while(aux.parentNode.nodeName.toLowerCase() != "form"){             
           aux = aux.parentNode;               
           target = procurarInput(aux);
           if (target != null) return target.focus();
       } 
   }
}

//Essa função procura pelo primeiro irmão input[type=text] subsequente a //target ou pela primeira ocorrencia de um input[type=text] aninhado
// em um div subsequente a target 
function procurarInput(target){
  var result = null;
   //Procura pelos irmãos subsequentes a target do tipo input[type=text] e div
   $(target).nextAll("input[type=text], div").each((i,elem)=>{    
        // Verifica se é um  input[type=text]  
        if (elem.nodeName.toLowerCase() == "input") {
          //Se é um input[type=text] o coloca em result e abandona o each
          result = elem; 
          return false
        }
        //Se não é um input[type=text] é um div então procura em
        //seus descendente pela primeira ocorrencia de um input[type=text]
        var alt = $(elem).find("input[type=text]:first");
        //Verifica se achou algum candidato
        if (alt.length > 0) {
          //Se achou o coloca em result e abandona o each
          result = alt;
          return false;
        }
   });
   return result  //retorna o resultado da busca.
}
<form class="container">
    <div>
        <span>input1</span>
        <input type="text" maxlength="5" />
        <input type="text" maxlength="5" />
    </div>                              
    <input type="text" maxlength="5" />
    <div>
        <span>input2</span>
        <input type="text" maxlength="5" />
        <div>
           <span>input2.5</span>
           <input type="text" maxlength="5" />
              <div>
                <span>input2.75</span>
                <input type="text" maxlength="5" />
             </div> 
        </div> 
    </div>  
    <div>
        <span>input3</span>
        <input type="text" maxlength="5" />
    </div>  
    <div>
        <span>input5</span>
        <input type="text" maxlength="5" />
    </div>  
    <div>
        <span>um div sem input</span>
    </div>
</form>

<div>
   <!-- Esse input não deve ser auto-selecionado, pois etá fora do formulário -->
   <span>input fora do formulário não recebe o auto-foco</span>
   <input type="text" maxlength="5" />
</div>  
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="script.js"></script>  

  • 1

    What if the input is inside a div, nestled the other div?

  • 1

    @Luizfelipe, and I was thinking about it. The time I get home I update the code

  • @Luizfelipe updated code.

  • 1

    Your code also works, too bad it got a little long, even so thank you.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.