1
votes

Using AG-Grid, I need to be able to hit the tab key and have the focused element change from the grid/cell currently selected, to the next element on the page outside of the grid. The problem is that the tab key seems to be locked within the grid, and will not move outside of the data table to the next element.

I have an even listener on the cells that stores the last focused cell (used to store the last location to be able to tab back into the grid to the previously focused cell), but need to have the next focused cell be outside of the data grid:

const cells = document.getElementsByClassName('ag-cell');
[...cells].map(cell => {
  cell.addEventListener("keydown", function(e) {
    if(e.key === "Tab") {
      let lastCell = cell.attributes[2].value;
      console.log("Last Cell Focused: ", lastCell)
    }
  })
})

How can I remove the focus selection from the grid on keypress to the next focusable page element?

Here's a plnkr link to the current grid: Link

=====================================================

UPDATE

I've updated my code, and instead of attaching an event listener to every cell, it's now looking for the event triggered on the document. However, I'm still running into the issue that it's not getting the last_cell value and seeing the focus-visible class on hasFocusVisible.

//on arrow right if last_call === header that has 'focus-visible', set focus to first cell in body
const headerCells = document.getElementsByClassName('ag-header-cell-label');
const last_cell = headerCells[headerCells.length-1].attributes[2];
const hasFocusVisible = document.querySelector('.ag-header-cell-label').classList.contains('focus-visible');
document.addEventListener("keydown", function(e) {
  if(e.key === "ArrowRight") {
    // if(hasFocusVisible && last_cell) {
      console.log("EVENT TRIGGERED FROM: ", event.target, "Last Cell Value: ", last_cell, hasFocusVisible);
      //if last_call and 'ag-header-cell-label' has 'focus-visible', set focus to first cell in body
      const bodyCell = document.getElementsByClassName('ag-cell')[0];
    // }
  }
});

UPDATED Plnkr: Link

====================================================

UPDATE 2

I've updated the element selector to the following:

const last_cell = document.querySelector('.ag-header-cell:last-child');
const hasFocusVisible = document.querySelector('.ag-header-cell-label').classList.contains('.focus-visible');
document.addEventListener("keydown", function(e) {
  console.log('document.activeElement', document.activeElement)
  const activeElement = document.activeElement;
  if(e.key === "ArrowRight" && activeElement) {
    if(last_cell) {
      console.log("EVENT TRIGGERED FROM: ", event.target, "Last Cell Value: ", last_cell, hasFocusVisible);
      //if last_call and 'ag-header-cell-label' has 'focus-visible', set focus to first cell in body
      const bodyCell = document.getElementsByClassName('ag-cell')[0];
    }
  }
  else if(e.key === "ArrowDown"){
    //look for first child in first row with same id as header and set focus
    document.querySelector('.ag-cell').focus();
  }
});

however, the hasFocusVisible variable is always coming up false when logging out the div that has the focus-visible class. I'm not sure if I have my logic incorrect, or its not able to get the focus-visible class on the ag-header-cell-label when the event listener is fired.

1
Is setting the tabindex HTML attribute not possible? Moz dev page for tabindex: developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/… - kevin628

1 Answers

0
votes

If tab works within the cells, don't add a listener to every cell, just add a single one to your document, and make it move focus to whatever you know is next on the page manually. For instance:

var b = document.querySelector('button');
b.passThrough = true;
b.update = pass => {
  b.passThrough = pass;
  b.textContent = "click me to " + (b.passThrough ? "block" : "allow") + " tabbing";
}
b.addEventListener('click', e => b.update(!b.passThrough));
b.update(b.passThrough);

var focussable = Array.from(
  document.querySelectorAll([
    'button',
    '[href]',
    'input',
    'select',
    'textarea',
    '[tabindex]:not([tabindex="-1"])'
  ].join(','))
);

// let's pretend this is your last cell.
var p = document.querySelector('p');

// make it kill off keydown events, BUT, also have it redirect focus
// to "the next focussable element", so you can see what that code looks like.
p.addEventListener('keydown', e => e.preventDefault());

document.addEventListener('keydown', e => {
  if (b.passThrough && e.target === p) {
    var next = focussable.indexOf(p) + 1;
    focussable[next % focussable.length].focus();
  }
});
<button>toggle</button>
<p tabindex=0>first</p>
<a href="#top">second</a>
<a href="#top">third</a>

Run this snippet, click the button, hit tab, notice that the tab event is now trapped (like in your cells). Now, click the button again, hit tab, hit tab again: notice it seems like the event is no longer trapped, when it fact it is: the event for the element itself is getting killed off, but the event listener for the document now explicitly moves focus for us.