Home > Software design >  focus/select next text field automatically
focus/select next text field automatically

Time:10-10

Here is a code that I am using in my current project. There are two fields to enter numbers each allows max 3 digits. I would like to automatically switch to second text field on completing first field with 3 digits. How can I do that ?

I found lots of example for input fields but as here I am using <td> I couldn't find a solution myself.

https://jsfiddle.net/shijilt/54918g3s/

<div >
<div >
<div >
<div id="add-product">
    <table cellpadding="10" cellspacing="1">
        <tbody>
            <tr>
                <th><h6 ><strong>Number</strong></h6></th>
                <th><h6 ><strong>Count</strong></h6></th>
            </tr>
            <tr>
        <td contentEditable="true" inputmode="numeric" data-id="lot_number_3" data-length="3" ><h3 ></h3></td>
        <td contentEditable="true" inputmode="numeric" data-id="lot_count_3"data-length="3" ><h3 >1</h3></td>
            </tr>
        </tbody>
    </table>
    

</div>

</div>
</div>

</div>
</div>
</div>
</div>
<script src="http://code.jquery.com/jquery-1.10.2.js"></script>
<script>
$('td[contentEditable="true"]').on("keydown", e => {
  // define allowed keys
  const allow = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'Tab', 'Backspace']
  // get pressed key
  const k = e.key
  if (allow.includes(k)) {
    // get the value before this keydown
    const val = e.target.textContent

    // determine the limit according to your logic
    const limit = Number(e.target.dataset['length']);

    if (val.length == limit && 'Tab' != k && 'Backspace' != k) {
      // pressing this key would put the value over the limit
      e.preventDefault()
      console.log('deny: '   k, val, limit)
    } else
      console.log('allow: '   k, val, limit)

  } else {
    // key not allowed
    e.preventDefault()
    console.log('deny: '   k)
  }

})
</script>

CodePudding user response:

Use trim() on val, because your div text contains many empty spaces: val.trim().length.

Then, you can move the focus on the next td with $(this).next('td')[0].focus() where you check the length of the input.

$('td[contentEditable="true"]').on("keydown", function(e) {
  // define allowed keys
  const allow = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'Tab', 'Backspace']
  // get pressed key
  const k = e.key
  if (allow.includes(k)) {
    // get the value before this keydown
    const val = e.target.textContent


    // determine the limit according to your logic
    const limit = Number(e.target.dataset['length']);



    if (val.trim().length == limit && 'Tab' != k && 'Backspace' != k) {
      // pressing this key would put the value over the limit
      e.preventDefault()
      console.log('deny: '   k, val, limit)
      if ($(this).next('td').length > 0) {
        $(this).next('td')[0].focus()
      }
    } else
      console.log('allow: '   k, val, limit)

  } else {
    // key not allowed
    e.preventDefault()
    console.log('deny: '   k)
  }

})
#add-product table {
  width: 100%;
  background-color: #F0F0F0;
}

#add-product table th {
  text-align: center;
}

#add-product table td {
  background-color: #FFFFFF;
  border-bottom: #F0F0F0 1px solid;
  text-align: center;
  word-break: break-all;
  max-width: 10px;
  vertical-align: top;
}

#list-product table {
  width: 100%;
}

#list-product table th {
  border-bottom: #F0F0F0 2px solid;
  text-align: left;
}

#list-product table td {
  border-bottom: #F0F0F0 1px solid;
  text-align: left;
  word-break: break-all;
  max-width: 10px;
  vertical-align: top;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div >
  <div >
    <div >
      <div id="add-product">
        <table cellpadding="10" cellspacing="1">
          <tbody>
            <tr>
              <th>
                <h6 ><strong>Number</strong></h6>
              </th>
              <th>
                <h6 ><strong>Count</strong></h6>
              </th>
            </tr>
            <tr>
              <td contentEditable="true" inputmode="numeric" data-id="lot_number_3" data-length="3" >
                <h3 ></h3>
              </td>
              <td contentEditable="true" inputmode="numeric" data-id="lot_count_3" data-length="3" >
                <h3 >1</h3>
              </td>
            </tr>
          </tbody>
        </table>


      </div>

    </div>
  </div>

</div>

CodePudding user response:

This is a late contribution to an already answered question. I am posting it here since I spent some time on putting it together.

The main effort went into accepting the non-ideal markup as it is (having contenteditable elements with nested elements inside them) and making it safe against any kind of user input. A common problem of contenteditable elements with nested internal structures is that the user can easily delete anything that is defined inside the structure. It would be very hard to provide safeguards against this from happening. Instead I decided to manipulate the DOM by transferring the contenteditable and other related attributes to the closest nested elements that don't have any children. It is these elements that I then collect in the array eds (editable elements).

In the second part I then add event listeners for the "input" event unto the elements of eds. I chose "input" over "keyup" as it also takes care of mouse triggerred paste events.

// Preprocessing: transfer contenteditable attributes to innermost child elements:
const eds=[...document.querySelectorAll("tbody [contenteditable=true]")].map(e=>{
  // find the innermost childless element:
  const ed=e.querySelector("*:not(:has(*))");
  if (ed){
   e.getAttributeNames().filter(a=>a!="class").forEach(a=>
    (ed.setAttribute(a,e.getAttribute(a)),
     e.removeAttribute(a)))
  }
  return ed ?? e;
});

eds.forEach((c,i)=>c.addEventListener("input",()=>{
 const l=c.dataset.length,cl=c.textContent.length;
 if(cl>=l){
  // set cursor into next editable field (at the end of the input string):
  setCaret(eds[(i 1)           
  • Related