detect outside table row click



How can I detect if a mouse click was not done on a table row?

I created this table, which can be modified on the fly and highlights the selected row, if selected. Now I try to establish a function, which removes the row highlighting as soon as a mouse click was not done in one of those table rows. I do not want to add another eventListener for the remaining body or other html elements located in the background.

I thought about the following:

if (row highlighted) { next outside click removes row highlight }

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    
    <title>Table Test</title>
    <!-- d3.js framework -->
    <script src="https://d3js.org/d3.v6.js"></script>
    <!-- jquery import-->
    <script   src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <!-- fontawesome stylesheet https://fontawesome.com/ -->
    <script src="https://kit.fontawesome.com/39094309d6.js" crossorigin="anonymous"></script>
</head>

<style>
    #myTable {
        border-collapse: collapse;
    }

    #myTable td {
        border: 1px solid black;
        padding: 8px;
    }

    #delRow {
        width: auto;
        height: 30px;
    }

</style>

<body>
    <table id="myTable">
        <th>
           Property 
        </th>
        <th>
            Value
        </th>
        <tbody>
            <!-- filled by script -->
        </tbody>
    </table>

    <button id="dataToArray">dataToArray</button>

    <script>
        data = [
            {
                "property": "animal",
                "value": "dog"
            },
            {
                "property": "car",
                "value": "audi"
            },
            {
                "property": "snacks",
                "value": "chips"
            }
        ]


        //populate table
        var myTable = document.getElementById("myTable")
        
        for (var i = 0; i < data.length; i++ ) {
            var row =   `<tr>
                            <td contenteditable='true'>${data[i].property}</td>
                            <td contenteditable='true'>${data[i].value}</td>
                        </tr>`

            myTable.innerHTML += row   
        }

        //higlight row
        let lastClickedRow = null
        for (var i = 0, tableRow; tableRow = myTable.rows[i]; i++) {
                tableRow.addEventListener("click", function() {
                    if (lastClickedRow != null) {
                        lastClickedRow.style.backgroundColor = null
                    }
    
                    lastClickedRow = this
    
                    lastClickedRow.style.backgroundColor = "greenyellow"
                })
            }

        //get table data
        var dataToArray = document.getElementById("dataToArray")

        dataToArray.addEventListener("click", function() {
            var exportData = []
            $("table#myTable tr").each(function() {
                var rowDataObject = new Object
                var actualData = $(this).find("td");
                if (actualData.length > 0) {
                        rowDataObject.property = actualData[0].innerText;
                        rowDataObject.value = actualData[1].innerText;
                        exportData.push(rowDataObject) 
                };
            })
            console.log(exportData)
        })
    </script>
</body>

</html>

Answer

Much simpler solution for this will be

  • Add an event listner to the click event of body.
  • Check whether the ckick event is either on the button or on the table with Node.contains in Javascript Reference
  • If the table row and button is not clicked, reset the color.

data = [
  {
    "property": "animal",
    "value": "dog"
  },
  {
    "property": "car",
    "value": "audi"
  },
  {
    "property": "snacks",
    "value": "chips"
  }
]


//populate table
var myTable = document.getElementById("myTable")

for (var i = 0; i < data.length; i++) {
  var row = `<tr>
                  <td contenteditable='true'>${data[i].property}</td>
                  <td contenteditable='true'>${data[i].value}</td>
              </tr>`
  myTable.innerHTML += row
}

//higlight row
let lastClickedRow = null;

function resetColor() {
  for (var i = 0, tableRow; tableRow = myTable.rows[i]; i++) {
    tableRow.style.backgroundColor = null;
  }
}

for (var i = 0, tableRow; tableRow = myTable.rows[i]; i++) {
  tableRow.addEventListener("click", function (e) {
    if (lastClickedRow != null) {
      lastClickedRow.style.backgroundColor = null
    }
    lastClickedRow = this
    lastClickedRow.style.backgroundColor = "greenyellow";
  })
}

var tableElement = document.getElementById('myTable');
var buttonElement = document.getElementById('dataToArray');


document.addEventListener('click', function(event) {
  const isInsideClicked = tableElement.contains(event.target) || buttonElement.contains(event.target);
  if(!isInsideClicked) {
    resetColor();
  }
})

//get table data
var dataToArray = document.getElementById("dataToArray")
dataToArray.addEventListener("click", function (e) {
  var exportData = []
  $("table#myTable tr").each(function () {
    var rowDataObject = new Object
    var actualData = $(this).find("td");
    if (actualData.length > 0) {
      rowDataObject.property = actualData[0].innerText;
      rowDataObject.value = actualData[1].innerText;
      exportData.push(rowDataObject)
    };
  })
  console.log(exportData)
})
#myTable {
  border-collapse: collapse;
}

#myTable td {
  border: 1px solid black;
  padding: 8px;
}

#delRow {
  width: auto;
  height: 30px;
}
<table id="myTable">
  <th>
    Property
  </th>
  <th>
    Value
  </th>
  <tbody>
    <!-- filled by script -->
  </tbody>
</table>

<button id="dataToArray">dataToArray</button>


Source: stackoverflow