skip to Main Content

I have a table and each row has one or more user inputs with a focusout event listener attached, that calls a function:

function inputFocusOut(event, el) {
     let row = el.closest('tr');
     let curFocus = event.relatedTarget;
     let moveOn = true;
     
     if(focus was on dynamically added row) {
          // I have another way of handling these
          moveOn = false;
     } else if(curFocus) {
          // Check if in same row
          if(in same row) {
               moveOn = false;
          }
     } else if(el.nodeName === 'INPUT' && el.type === 'file') {
          // If focus on file picker, moveOn = false;
     }

     if(moveOn) {
          // Check if values changed and do more stuff if anything did change
     }
}

The function works great for everything except input[type='file']. When you click to upload a file, the focus is on the file picker dialogue, so the input loses focus, triggering the function.

Is it possible to know when focus is on the file browser window, in which case I don’t want to continue with the rest of the logic? The event.relatedTarget is undefined when the file picker is open, so I’m not sure what I would check.

I found this question, but the answer talks about putting focus on the input and blurring it, which I’m already doing (albeit with focuout instead of blur). Haven’t been able to find anything helpful, which could mean I’m not use the best search terms.

Any help in JavaScriptor jQuery would be appreciated.

2

Answers


  1. Chosen as BEST ANSWER

    Thanks to @chrwahl for pushing me in the right direction. I ended up adding another condition to my focusout function specifically for input[type='file'] that works like a charm. Here's what I ended up doing:

    var currentRow = 0;
    function inputFocus(el) {
         let thisRow = el.closest('tr');
         currentRow = thisRow.dataset.id;
    }
    
    function inputFocusOut(event, el) {
         let thisRow = el.closest('tr');
         let curFocus = event.relatedTarget;
         let updateRecord = true;
    
         if(currentRow === 0) {
              updateRecord = false;
         } else if(curFocus) {
              if(curFocus.closest('tr').dataset.id === currentRow) {
                   updateRecord = false;
              }
         } else if(el.nodeName === 'INPUT' && el.type === 'file') {
              if(thisRow.dataset.id === currentRow) {
                   updateRecord = false;
              }
         }
    
         if(updateRecord) {
              console.log('Check if values changed');
         }
    }
    <table class='table'>
      <thead>
           <tr>
                <th>Input 1</th>
                <th>Input 2</th>
           </tr>
      </thead>
      <tbody>
           <tr data-id='1'>
             <td><input type='text' onfocus='inputFocus(this);' onfocusout='inputFocusOut(event, this);'></td>
             <td><input type='file' onfocus='inputFocus(this);' onfocusout='inputFocusOut(event, this);'></td>
           </tr>
           <tr data-id='2'>
             <td><input type='text' onfocus='inputFocus(this);' onfocusout='inputFocusOut(event, this);'></td>
             <td><input type='file' onfocus='inputFocus(this);' onfocusout='inputFocusOut(event, this);'></td>
          </tr>
        </tbody>
    </table>


  2. The input[file] has the event cancel that you can listen for. So, here I created a function exitHander that handles different events on different input types. The exitHandler is the callback function for both blur, cancel and change. If the target is input[file] and the event type is either cancel or change then it’s a "blur". When the target is any other input than file it is also a "blur".

    var currentrow = null;
    const form = document.forms.form01;
    
    form.addEventListener('focus', enterHandler, true);
    form.addEventListener('blur', exitHandler, true);
    form.addEventListener('cancel', exitHandler, true);
    form.addEventListener('change', exitHandler, true);
    
    function enterHandler(e) {
      let row = e.target.closest('tr');
      if (row !== currentrow) {
        currentrow = row;
      }
    }
    
    function exitHandler(e) {
      let row = e.target.closest('tr');
      if (e.target.type == 'file' && e.type == 'change' || e.type == 'cancel') {
        console.log('input[file] "blur"');
      } else if (e.target.type != 'file') {
        console.log(`input[${e.type}] "blur"`);
      }
    }
    tr:focus-within {
      background-color: silver;
    }
    <form name="form01">
      <table>
        <tr>
          <td><input type="checkbox" name="r1_checkbox"></td>
          <td><input type="text" name="r1_text"></td>
          <td><input type="file" name="r1_file"></td>
        </tr>
        <tr>
          <td><input type="checkbox" name="r2_checkbox"></td>
          <td><input type="text" name="r2_text"></td>
          <td><input type="file" name="r2_file"></td>
        </tr>
        <tr>
          <td><input type="checkbox" name="r3_checkbox"></td>
          <td><input type="text" name="r3_text"></td>
          <td><input type="file" name="r3_file"></td>
        </tr>
      </table>
    </form>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search