Highlight selected cells of a table using React

Let's assume that you want to highlight the cells of a table using React. The starting point is to create the component and to render a table:

jsx
import React from 'react';

const SuperTable = () => {
    
    const data = [
        
        ['Erik',   30, 'Barcelona'],
        ['Andrea', 27, 'Barcelona'],
        ['Paula',  16, 'Calella']
        
    ]
    
    return(
        <table>
            <tbody>
                { data.map((row, i) => 
                    <tr>{row.map((elem, j) => 
                        <td>{elem}</td>)}
                    </tr>
                )}
            </tbody>
        </table>
    );
    
}

export default SuperTable;

The previous example will display a normal table, nothing special here. Now it's time to add the sauce. 🥫

Adding handlers and variables

Use an array to save the start and the end position of the selection.

The array can be: [iniRow, iniCol, endRow, endCol].

Define a variable to check if the user is dragging the table. Finally, define 3 handlers to implement the desired actions on dragging.

jsx
import React, { useState } from 'react';

const SuperTable = () => {
    
    const data = [
        
        ['Erik',   30, 'Barcelona'],
        ['Andrea', 27, 'Barcelona'],
        ['Paula',  16, 'Calella']
        
    ]
    
    // This array defines [iniRow, iniCol, endRow, endCol]
    const [select, setSelect] = useState([0, 0, 0, 0]);
    
    // This variable will control if the user is dragging or not
    const [drag, setDrag] = useState(false);
    
    const handleMouseDown = (e, row, col) => {
        
        // User starts to drag here
        
    }
    
    const handleMouseUp = (e, row, col) => {
        
        // User stops selecting and leaves the mouse
        
    }
    
    const handleMultipleSel = (e, row, col) => {
        
        // User is moving the mouse here
        
    }
    
    return(
        <table>
            <tbody>
                { data.map((row, i) => 
                    <tr>{row.map((elem, j) => 
                        <td
                            onMouseDown = {(e) => handleMouseDown(e, i, j)}
                            onMouseUp   = {(e) => handleMouseUp(e, i, j)}
                            onMouseMove = {(e) => handleMultipleSel(e, i, j)}
                            className   = {getClassName(i, j)}
                        >{elem}</td>)}
                    </tr>
                )}
            </tbody>
        </table>
    );
    
}

export default SuperTable;

Defining the handlers

When the users click on a cell, the following function will execute (notice how you add the same cell as starting and ending point):

jsx
const handleMouseDown = (e, row, col) => {
    
    setDrag(true);
    setSelect([row, col, row, col]);
    
}

When the user stops dragging, update the state:

jsx
const handleMouseUp = (e, row, col) => {

    setDrag(false);

}

While the user is dragging, update the starting and ending point. Notice that depending on the user dragging direction, you'll modify the selection order.

Again, the goal is to always have an array like this one:

[iniRow, iniCol, endRow, endCol]

The ending position is greater than the starting position.

jsx
const handleMultipleSel = (e, row, col) => {
    
    e.preventDefault();
    
    if(drag){
    
        let [iniRow, iniCol, endRow, endCol] = select;
        
        if(iniRow <= row && iniCol <= col)
            setSelect([iniRow, iniCol, row, col]);
        
        if(iniRow >= row && iniCol >= col)
            setSelect([row, col, endRow, endCol]);
        
    }
    
}

Defining the classes

If a cell on the table is in between the interval defined by the select array, set its className = 'Selected'.

jsx
const getClassName = (row, col) => {
    
    let [iniRow, iniCol, endRow, endCol] = select;
    
    if(row >= iniRow && row <= endRow && col >= iniCol && col <= endCol){
        return 'Selected';
    }
    
}

Defining the CSS

Finally, add some CSS to highlight selected cells.

css
.Selected{
    background: blue;
    user-select: none;
}

Hi, I'm Erik, an engineer from Barcelona. If you like the post or have any comments, say hi.