2016-06-24 22 views
16

Ho un semplice problema con React e gestione degli eventi. La mia componente si presenta così (in pratica una tabella):Evento di reazione e sfocatura

const MyList = ({ items, onBlur }) => 
<table onBlur={onBlur}}> 
    <thead> 
    <tr> 
     <th>ID</th> 
     <th>Title</th> 
     <th>Publisher</th> 
     <th>Year</th> 
     <th>Author</th> 
     <th>System</th> 
     <th/> 
    </tr> 
    </thead> 
    <tbody> 
    {items.map(item => <MyListRow key={item.Id} item={item}/>)} 
    </tbody> 
</table>; 

voglio l'evento blur al fuoco solo se la messa a fuoco va fuori del tavolo. Invece l'evento si attiva su ogni elemento figlio della tabella quando perde lo stato attivo.

Secondo i documenti, React consente di evidenziare gli eventi di messa a fuoco.

La domanda è: come posso attivare il metodo onBlur solo quando lo stato attivo esce dalla tabella? IOW: Come posso filtrare e scartare gli eventi indesiderati che ribolliscono in modo da rivelare solo gli eventi che indicano una perdita di concentrazione per il tavolo?

risposta

13

Il problema è che una tabella in realtà non ha un concetto di messa a fuoco poiché non è un input stesso.

Quando i fuochi onBlur sugli ingressi contenute che controllerà la relatedTarget dell'evento onBlur che deve essere impostato con l'elemento che ha ricevuto attenzione (o null). Quindi usiamo una funzione che attraverserà verso l'alto attraverso parentNode da quel nuovo elemento focalizzato e assicuriamo che il nostro evento currentTarget (la tabella) non sia un antenato dell'elemento appena focalizzato. Se la condizione passa, si presume che la tabella non abbia più alcun focus.

const focusInCurrentTarget = ({ relatedTarget, currentTarget }) => { 
 
    if (relatedTarget === null) return false; 
 
    
 
    var node = relatedTarget.parentNode; 
 
     
 
    while (node !== null) { 
 
    if (node === currentTarget) return true; 
 
    node = node.parentNode; 
 
    } 
 

 
    return false; 
 
} 
 

 
const onBlur = (e) => { 
 
    if (!focusInCurrentTarget(e)) { 
 
    console.log('table blurred'); 
 
    } 
 
} 
 

 
const MyList = ({ items, onBlur }) => (
 
    <table onBlur={onBlur}> 
 
    <thead> 
 
     <tr> 
 
     <th>ID</th> 
 
     <th>Title</th> 
 
     <th>Publisher</th> 
 
     <th>Year</th> 
 
     <th>Author</th> 
 
     <th>System</th> 
 
     <th/> 
 
     </tr> 
 
    </thead> 
 
    <tbody> 
 
     <tr> 
 
     <td>1</td> 
 
     <td> 
 
      <input type="text" /> 
 
     </td> 
 
     <td> 
 
      <input type="text" /> 
 
     </td> 
 
     <td> 
 
      <input type="text" /> 
 
     </td> 
 
     <td> 
 
      <input type="text" /> 
 
     </td> 
 
     <td> 
 
      <input type="text" /> 
 
     </td> 
 
     </tr> 
 
    </tbody> 
 
    </table> 
 
); 
 
    
 
ReactDOM.render(
 
    <MyList onBlur={onBlur} />, 
 
    document.getElementById('root') 
 
);
table { 
 
    padding: 10px; 
 
    border: 1px solid red; 
 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> 
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> 
 
<div id="root"></div> 
 
<br /> 
 
<input type="text" />

Riferimenti:

AGGIORNAMENTO:

Eliminato l'uso di ReactDOM.findDOMNode

+0

Non sarebbe possibile semplicemente racchiudere la 'tabella' in un' div' e applicare 'onBlur' a' div'? Non si chiamerebbe quando rimuovete il focus dalla 'table' se il' div' comprende solo il 'table'? – Fizz

+0

Non penso che sarebbe diverso dall'usare l'elemento table come target di onBlur poiché nessuno dei due ha un concetto di focus; dovrai comunque controllare se si tratta di un bambino/genitore. Sebbene dopo aver nuovamente esaminato la mia soluzione, mi è venuta un'idea più pulita che evita ReactDOM.findDOMNode! Aggiornare ora. – TheZanke

+0

Ho capito cosa intendi, anche se puoi sempre impostare il 'tabindex' di' div' su 0: P – Fizz

4

Senza funzioni personalizzate e Internet Explorer compatibile, dal momento che node.contains e document.activeElement sono supportati dal Internet Explorer 5, questo funziona:

const onBlur = (e) => { if (!e.currentTarget.contains(document.activeElement)) { console.log('table blurred'); } }

  • I Node.contains() restituisce un valore booleano che indica se un nodo è un discendente di un dato nodo oppure no.
  • Document.activeElement restituisce l'elemento attualmente focalizzato, ovvero l'elemento che otterrà eventi di battitura se l'utente ne scrive uno.
+1

mia modifica: se (event.relatedTarget && event.currentTarget.contains ( event.relatedTarget)) { \t ritorno console.log ('non sulla sfocatura') } – asdfasdfads

+2

Nel mio uso, 'onBlur' è sempre chiamato con '' come elemento attivo, come [questa domanda] (https://stackoverflow.com/questions/11299832/document-activeelement-returns-body) – Felipe

+1

Come Felipe, 'document.activeElement' è il' '. Invece controllo 'relatedTarget' per vedere se è figlio del' currentTarget' come se ... 'if (e.relatedTarget && e.currentTarget.contains (e.relatedTarget)) { console.log (" figlio cliccato "); } ' – wintondeshong