skip to Main Content

I created a app using the Google JavaScript Maps API to determine the distance between addresses. I currently have a bug. When the user clicks the row on the table it should highlight the selected row by calling this function: highlight_row().

When testing it does not highlight the row on the first click. It needs to be clicked a second time for it to highlight the row. Why does this function not style the row on the first click? Any help or guidance would be greatly appreciated. Thanks

When I add the highlight_row() function to the global scope, and then call it separately on the onClick I get the same result.

I also have tried adding the onclick inside the span on the table. This only allows the onclick to fire off when the display name cell is clicked. Adding the onClick in the span does style the selected row on the first click. Why does this work when in the tag and not in the tag?

I will add the code below. You will need to add your own google maps api key to test locally.

<!DOCTYPE html>
<html>
  <head>
    <title>Distance Calculation</title>
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_GOOGLE_MAPS_API_KEY&libraries=places"></script>
    <style>
      table {
        border-collapse: collapse;
        width: 100%;
      }

      th,
      td {
        border: 1px solid #ddd;
        padding: 8px;
        text-align: left;
      }

      th {
        background-color: #f2f2f2;
      }

      tr.highlighted {
        background-color: blue;
        color: white; /* For better visibility */
      }
    </style>
    <script>
      const destinationAddresses = {
        destination1: {
          Tour_Location: "Embassy Suites by Hilton Parsippany",
          Display_Name: "Hilton Parsippany",
          Address1: "909 Parsippany Blvd",
          Address2: "",
          City: "Parsippany",
          State: "NJ",
          Zip: "07054",
          Tour_Location_Record_Id: "a1F1U000003VKSSUA4",
          Phone: "(631) 260-1849",
        },
        destination2: {
          Tour_Location: "Roosevelt Field Mall",
          Display_Name: "Roosevelt Field Mall",
          Address1: "2nd Floor - Next to Nordstrom",
          Address2: "630 Old Country Rd",
          City: "Garden City",
          State: "NY",
          Zip: "11530",
          Tour_Location_Record_Id: "a1F1U000003UG8NUAW",
          Phone: "(631) 260-1849",
        },
        destination3: {
          Tour_Location: "Club Wyndham Midtown 45",
          Display_Name: "Midtown 45",
          Address1: "205 E 45th St",
          Address2: "",
          City: "New York",
          State: "NY",
          Zip: "10017",
          Tour_Location_Record_Id: "a1F1U000003UbX8UAK",
          Phone: "(555) 555-5555",
        },
        // Add more destination objects if needed
      };

      function getAddress(destination) {
        return `${destination.Address1} ${destination.Address2}, ${destination.City}, ${destination.State} ${destination.Zip}`;
      }

      function SetTourLocationRecordId(recordId) {
        console.log(`Tour Location Record Id: ${recordId}`);
        const dataArray = recordId.split(",");
        const splitData = dataArray.map((data) => data.trim());

        const set_Tour_Location_Record_Id = splitData[0]; // Output: a1F1U000003VKSSUA4
        const set_Tour_Location = splitData[1]; // Embassy Suites by Hilton Parsippany
        const set_Display_Name = splitData[2]; // Output: Hilton Parsippany
        const set_Address1 = splitData[3]; // Output: 909 Parsippany Blvd
        const set_Address2 = splitData[4]; // Output: Address2
        const set_State = splitData[5]; // Output: NJ
        const set_City = splitData[6]; // Output: Parsippany
        const set_Zip = splitData[7]; // Output: 07054
        const set_Phone = splitData[8]; // Output: 07054

        alert(`You have selected ${set_Tour_Location}`);

        console.log(set_Tour_Location_Record_Id); // Output: a1F1U000003VKSSUA4
        console.log(set_Tour_Location); // Output: Embassy Suites by Hilton Parsippany
        console.log(set_Display_Name); // Output: Hilton Parsippany
        console.log(set_Address1); // Output: 909 Parsippany Blvd
        console.log(set_Address2); // Output:
        console.log(set_State); // Output: NJ
        console.log(set_City); // Output: Parsippany
        console.log(set_Zip); // Output: 07054
        console.log(set_Phone); // Output: 07054

        // set row color
        highlight_row();
        function highlight_row() {
          var table = document.getElementById("display-table");
          var rows = table.getElementsByTagName("tr");

          for (var i = 0; i < rows.length; i++) {
            rows[i].addEventListener("click", function () {
              // Reset styles for all rows
              for (var j = 0; j < rows.length; j++) {
                rows[j].style.backgroundColor = "";
                rows[j].style.color = "";
              }

              // Set styles for the clicked row
              this.style.backgroundColor = "#1e90ff";
              this.style.color = "snow";
            });
          }
        }
      }

      function calculateDistance() {
        const address1 = document.getElementById("address1").value;

        const geocoder = new google.maps.Geocoder();

        geocoder.geocode({ address: address1 }, function (results1, status1) {
          if (status1 === google.maps.GeocoderStatus.OK) {
            const origin = results1[0].geometry.location;

            const distanceService = new google.maps.DistanceMatrixService();
            distanceService.getDistanceMatrix(
              {
                origins: [origin],
                destinations: Object.values(destinationAddresses).map(
                  (destination) => getAddress(destination)
                ),
                travelMode: google.maps.TravelMode.DRIVING,
                unitSystem: google.maps.UnitSystem.IMPERIAL, // Use miles
              },
              function (response, status) {
                if (status === google.maps.DistanceMatrixStatus.OK) {
                  let resultHtml = "<table id=display-table>";
                  resultHtml +=
                    "<tr><th>Display Name</th><th>City</th><th>State</th><th>Zip</th><th>Distance</th><th>Drive Time</th></tr>";
                  for (const key in destinationAddresses) {
                    if (destinationAddresses.hasOwnProperty(key)) {
                      const destination = destinationAddresses[key];
                      const address = getAddress(destination);
                      const distance =
                        response.rows[0].elements[
                          parseInt(key.split("destination")[1]) - 1
                        ].distance.text;
                      const duration =
                        response.rows[0].elements[
                          parseInt(key.split("destination")[1]) - 1
                        ].duration.text;
                      resultHtml += `<tr onclick="SetTourLocationRecordId('${destination.Tour_Location_Record_Id}, ${destination.Tour_Location}, ${destination.Display_Name}, ${destination.Address1}, ${destination.Address2}, ${destination.State}, ${destination.City}, ${destination.Zip}, ${destination.Phone}');"><td> <span >${destination.Display_Name}</td></span><td>${destination.City}</td><td>${destination.State}</td><td>${destination.Zip}</td></td><td>${distance}<td>${duration}</td></tr>`;
                    }
                  }
                  resultHtml += "</table>";
                  if (resultHtml) {
                    document.getElementById("result").innerHTML = resultHtml;
                  } else {
                    document.getElementById("result").innerHTML =
                      "Unable to calculate distance for all addresses.";
                  }
                } else {
                  document.getElementById("result").innerHTML =
                    "Error calculating distance.";
                }
              }
            );
          } else {
            document.getElementById("result").innerHTML =
              "Invalid origin address.";
          }
        });
      }
    </script>
  </head>
  <body>
    <h1>Distance Calculation</h1>
    <label for="address1">Origin Address:</label>
    <input type="text" id="address1" />
    <br />
    <button onclick="calculateDistance()">Calculate Distance</button>
    <br />
    <div id="result"></div>
  </body>
</html>

2

Answers


  1. This happens because you are calling highlight_row() function before you have defined it. Moving highlight_row() function before you call it should solve your problem:

    function SetTourLocationRecordId(recordId) {
            console.log(`Tour Location Record Id: ${recordId}`);
            const dataArray = recordId.split(",");
            const splitData = dataArray.map((data) => data.trim());
            // move your function here
            function highlight_row() {
              var table = document.getElementById("display-table");
              var rows = table.getElementsByTagName("tr");
    
              for (var i = 0; i < rows.length; i++) {
                rows[i].addEventListener("click", function () {
                  // Reset styles for all rows
                  for (var j = 0; j < rows.length; j++) {
                    rows[j].style.backgroundColor = "";
                    rows[j].style.color = "";
                  }
    
                  // Set styles for the clicked row
                  this.style.backgroundColor = "#1e90ff";
                  this.style.color = "snow";
                });
              }
            }
            //...
            // and call it after declaring
            function highlight_row();
    

    The reason because your function worked only after 2nd click, was that on first click program didn’t know anything about hightlight_row() function, other than that it just exists somewhere in memory. Because JS run code once, it already know about highlight_row() function, so on 2nd and further clicks your code worked.

    Login or Signup to reply.
  2. I would be tempted to modify the original code slightly as below to remove inline function calls and instead use externally registered event handlers. It seems that your SetTourLocationRecordId does lots of things but nothing really useful is done with the data passed in so an alternative approach below might be of interest.

    <!DOCTYPE html>
    <html>
      <head>
        <title>Distance Calculation</title>
    
        <style>
          table {
            border-collapse: collapse;
            width: 100%;
          }
    
          th,
          td {
            border: 1px solid #ddd;
            padding: 8px;
            text-align: left;
          }
    
          th {
            background-color: #f2f2f2;
          }
    
          tr.highlighted {
            background-color: blue;
            color: white; /* For better visibility */
          }
          
          label,input,button{padding:0.25rem}
          
          /* custom class to add to highlighted table rows... probably same as above? */
          .row_highlight{
            background:#1e90ff;
            color:snow;
          }
        </style>
        <script>
          const destinationAddresses = {
            destination1: {
              Tour_Location: "Embassy Suites by Hilton Parsippany",
              Display_Name: "Hilton Parsippany",
              Address1: "909 Parsippany Blvd",
              Address2: "",
              City: "Parsippany",
              State: "NJ",
              Zip: "07054",
              Tour_Location_Record_Id: "a1F1U000003VKSSUA4",
              Phone: "(631) 260-1849",
            },
            destination2: {
              Tour_Location: "Roosevelt Field Mall",
              Display_Name: "Roosevelt Field Mall",
              Address1: "2nd Floor - Next to Nordstrom",
              Address2: "630 Old Country Rd",
              City: "Garden City",
              State: "NY",
              Zip: "11530",
              Tour_Location_Record_Id: "a1F1U000003UG8NUAW",
              Phone: "(631) 260-1849",
            },
            destination3: {
              Tour_Location: "Club Wyndham Midtown 45",
              Display_Name: "Midtown 45",
              Address1: "205 E 45th St",
              Address2: "",
              City: "New York",
              State: "NY",
              Zip: "10017",
              Tour_Location_Record_Id: "a1F1U000003UbX8UAK",
              Phone: "(555) 555-5555",
            },
            // Add more destination objects if needed
          };
          
          
          
          
          
          function init(){
            const geocoder = new google.maps.Geocoder(); // only need to declare this once!
            const getAddress=(o)=>`${o.Address1} ${o.Address2}, ${o.City}, ${o.State} ${o.Zip}`;
            
            /*
                What does this function actually do???
            */      
            const SetTourLocationRecordId=(e)=>{
                const json=JSON.parse( e.target.closest('tr').dataset.json );
                if( Object.keys( json ).length > 0 ){
                    Object.keys( json ).forEach(key=>{
                        console.log( 'Key:%s, Value:%s',key, json[key] )
                    });
                }
            };
            const highlight_row=(e)=>{
                let table=e.target.closest('table');// find the parent table from the click event target
                table.querySelectorAll('tr').forEach(tr=>{//remove assigned className from each table-row
                    tr.classList.remove('row_highlight');
                });
                e.target.closest('tr').classList.add('row_highlight')// add chosen className to this table row
            };
            /* 
                define an event handler - this enables us to remove it. 
                You could do this in other ways but this is relatively clean.
            */
            const tableclickhandler=(e)=>{
                if( e.target!=e.currentTarget && e.target instanceof HTMLTableCellElement ){
                    SetTourLocationRecordId(e);
                    highlight_row(e);
                }
            }
    
            function calculateDistance() {
                const address1 = document.querySelector('input[name="address"]').value;
                const div=document.getElementById('result');
                
    
                geocoder.geocode({ address: address1 }, function (results1, status1) {
                  if (status1 === google.maps.GeocoderStatus.OK) {
                    const origin = results1[0].geometry.location;
                    const distanceService = new google.maps.DistanceMatrixService();
                    
                    distanceService.getDistanceMatrix(
                      {
                        origins: [origin],
                        destinations: Object.values(destinationAddresses).map(
                          (destination) => getAddress(destination)
                        ),
                        travelMode: google.maps.TravelMode.DRIVING,
                        unitSystem: google.maps.UnitSystem.IMPERIAL, // Use miles
                      },
                      function (response, status) {
                        if( status === google.maps.DistanceMatrixStatus.OK ) {
                            
                          // remove the click handler to prevent multiple firings of the code.
                          div.removeEventListener('click',tableclickhandler)
                            
                          let resultHtml = "<table id='display-table'>";
                              resultHtml +=`<tr>
                                    <th>Display Name</th>
                                    <th>City</th>
                                    <th>State</th>
                                    <th>Zip</th>
                                    <th>Distance</th>
                                    <th>Drive Time</th>
                                </tr>`;
                            
                            
                          for( const key in destinationAddresses ) {
                            if( destinationAddresses.hasOwnProperty(key) ) {
                              const destination = destinationAddresses[key];
                              const address = getAddress( destination );
                              const distance = response.rows[0].elements[ parseInt( key.split("destination")[1] ) - 1 ].distance.text;
                              const duration = response.rows[0].elements[ parseInt( key.split("destination")[1] ) - 1 ].duration.text;
                              /*
                              
                                Rather than adding each item as a parameter to the SetTourLocationRecordId
                                function you could simply supply the destination as a JSON string as a 
                                dataset attribute.
                                
                                Within the SetTourLocationRecordId function you can then access that dataset value
                                - though the original did nothing useful as far as I could see other than call the
                                highlight_row function. That highlight_row function has been simplified and uses
                                a className/classList rather than inline styles.
                                
                              */
                              
                              const json=JSON.stringify( destination );
                              
                              
                              
                              resultHtml += `
                                <tr data-json='${json}'>
                                    <td><span>${destination.Display_Name}</span></td>
                                    <td>${destination.City}</td>
                                    <td>${destination.State}</td>
                                    <td>${destination.Zip}</td>
                                    <td>${distance}</td>
                                    <td>${duration}</td>
                                </tr>`;
                            }
                          }
                          resultHtml += "</table>";
                          
                          if( resultHtml ) {
                          
                            div.innerHTML = resultHtml;
                            div.addEventListener('click',tableclickhandler )
                            
                          } else {
                            div.innerHTML = "Unable to calculate distance for all addresses.";
                          }
                          
                          
    
                          
                        } else {
                          div.innerHTML = "Error calculating distance.";
                        }
                      }
                    );
                  } else {
                    div.innerHTML = "Invalid origin address.";
                  }
                });
              }  
          
            document.querySelector('button[name="calc"]').addEventListener('click',calculateDistance );
          }
        </script>
      </head>
      <body>
      
        <h1>Distance Calculation</h1><!-- hardcoded address for testing only -->
        <label>Origin Address: <input type='text' name='address' value='Veterans Memorial Park, 1839 US-46, Parsippany, NJ 07054, United States' /></label>
        <button name='calc'>Calculate Distance</button>
        <div id='result'></div>
        
        <script async defer src='//maps.googleapis.com/maps/api/js?key=AIzaSy...&libraries=places&callback=init'></script>
      </body>
    </html>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search