Is there a way to use callback functions in Python with requests? This is from a here API example im trying to copy using python. This is the request and the function used at the end in the parameters is defined jsoncallback=parseResponse.
If i can replace the functionality of the callback in a other way would be fine too, is it possible to just take the response and do everything thats done in parseRespone?
"https://route.api.here.com/routing/7.2/calculateroute.json?jsonAttributes=1&waypoint0=50.7799,6.08425&waypoint1=50.77988,6.08288&waypoint2=50.78144,6.07794&representation=overview&routeattributes=sc,sm,sh,bb,lg,no,shape&legattributes=li&linkattributes=sh,nl,fc&mode=fastest;car;traffic:enabled&app_id=inCUge3uprAQEtRaruyaZ8&app_code=9Vyk_MElhgPCytA7z3iuPA&jsoncallback=parseResponse"
var parseResponse = function (resp)
{
if (resp.error != undefined)
{
alert (resp.error);
feedbackTxt.innerHTML = resp.error;
return;
}
if (resp.response == undefined)
{
alert (resp.subtype + " " + resp.details);
feedbackTxt.innerHTML = resp.error;
return;
}
//add Routing Release number if not already done
if (releaseRoutingShown == false){
releaseInfoTxt.innerHTML+="<br />HLP Routing: "+resp.response.metaInfo.moduleVersion;
routerMapRelease = resp.response.metaInfo.mapVersion;
mapReleaseTxt.innerHTML = "HLP Routing Service based on "+routerMapRelease+ " map release";
releaseRoutingShown = true;
}
var strip = new H.geo.Strip(),
shape = resp.response.route[0].shape,
i,
l = shape.length;
for(i = 0; i < l; i++)
{
strip.pushLatLngAlt.apply(strip, shape[i].split(',').map(function(item) { return parseFloat(item); }));
}
polyline = new H.map.Polyline(strip,
{
style:
{
lineWidth: 5,
strokeColor: "rgba(18, 65, 145, 0.7)",
lineJoin: "round"
}
});
group.addObject(polyline);
var links = [];
for(var i = 0; i < resp.response.route[0].leg.length; i++)
links = links.concat(resp.response.route[0].leg[i].link);
pdeManager.setLinks(links);
pdeManager.setBoundingBoxContainer(group);
pdeManager.setOnTileLoadingFinished(pdeManagerFinished);
pdeManager.start();
}
function pdeManagerFinished(finishedRequests)
{
feedbackTxt.innerHTML = "Done. Requested " + finishedRequests + " PDE tiles for " + numLinksMatched + " route links. ";
var resultHTML = '<table class="pde_table" cellpadding="2" cellspacing="0" border="1" width="90%">' +
'<thead>' +
'<tr>' +
'<th width="80%">Sign</th>' +
'<th width="20%">#</th>' +
'</tr>' +
'</thead>' +
'<tbody id="maps_table_body">';
for(var sign in signs)
{
resultHTML += "<tr>" + "<td>" + sign + "</td>" + "<td>" + signs[sign] + "</td>" + "</tr>";
}
resultHTML += "</tbody>" + "</table>";
document.getElementById("resultArea").innerHTML = resultHTML;
document.getElementById("resultArea").style.display = "block";
map.addObject(group);
map.setViewBounds(group.getBounds());
}
You don't need to use any callback (since there's nothing that would execute it anyway); elide the parameter and call response.json().
The below code does raise
Unauthorized. The request is not from an authorized source.
so you may need some additional headers or such (possibly an Origin header if the credentials are matched to a site address).
import requests
resp = requests.get(
url="https://route.api.here.com/routing/7.2/calculateroute.json",
params={
"jsonAttributes": "1",
"waypoint0": "50.7799,6.08425",
"waypoint1": "50.77988,6.08288",
"waypoint2": "50.78144,6.07794",
"representation": "overview",
"routeattributes": "sc,sm,sh,bb,lg,no,shape",
"legattributes": "li",
"linkattributes": "sh,nl,fc",
"mode": "fastest;car;traffic:enabled",
"app_id": "inCUge3uprAQEtRaruyaZ8",
"app_code": "9Vyk_MElhgPCytA7z3iuPA",
},
)
# Uncomment this to see the actual error.
# print(resp.content)
resp.raise_for_status()
print(resp.json())
Related
I'm working through the Google Place API documentation and I'm trying to get a script that pulls PlaceIDs from a webpage, and replace them with output from the Google Place API.
I managed to successfully get an output from multiple Place IDs by duplicating the code and changing the variable and function names, but now I'm trying to create a loop function so that I'm not duplicating code. Below is what I have, but I'm getting an error. By looking at the console, it seems to work up till the Callback function where it beaks down.
"Uncaught TypeError: Cannot set property 'innerHTML' of null
at callback (places.html:29)"
I've tried a few things, but no luck so far. Any suggestions would be appreciated. Thanks,
<body>
<div id="MY0">ChIJaZ6Hg4iAhYARxTsHnDFJ9zE</div>
<div id="MY1">ChIJT9e323V644kRR6TiEnwcOlA</div>
<script>
var request = [];
var service = [];
var div = [];
for (i = 0; i < 2; i++) {
request[i] = {
placeId: document.getElementById("MY" + i).innerHTML,
fields: ['name', 'rating', 'formatted_phone_number', 'geometry', 'reviews', 'photos'],
};
service[i] = new google.maps.places.PlacesService(document.createElement('div'));
service[i].getDetails(request[i], callback);
function callback(place, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
div[i] = document.getElementById("MY" + i);
div[i].innerHTML = "<b>" + place.name + "</b><br>" + place.rating + "<br>" + place.reviews[1].author_name + "<br>" + place.reviews[1].rating + "<br>" + place.reviews[1].text + "<br><img src='" + place.photos[0].getUrl({'maxWidth': 250, 'maxHeight': 250}) + "'>";
}
}
}
</script>
</body>
Move the callback outside of the for loop and forget about the array named div (unless you need this...if so I will rewrite). The for loop is executing before the getDetails() call returns any result, because this call is asynchronous - since you don't have much control over the Google Places callback, I would save the IDs in an array and then use them in callback, like this:
function gp_callback(place, status) {
var el = document.getElementById(window.id_set[0]); // first in first out - the for loop should populate the IDs in correct order
if (status == google.maps.places.PlacesServiceStatus.OK) {
el.innerHTML = "<b>" + place.name + "</b><br>" + place.rating + "<br>" + place.reviews[1].author_name + "<br>" + place.reviews[1].rating + "<br>" + place.reviews[1].text + "<br><img src='" + place.photos[0].getUrl({'maxWidth': 250, 'maxHeight': 250}) + "'>";
}
if (window.id_set.length > 1) {
window.id_set.splice(0, 1); // remove first element from array because has been used - now the next element is at index 0 for the next async callback
}
}
var request = [];
var service = [];
var id_set = [];
for (i = 0; i < 2; i++) {
request[i] = {
placeId: document.getElementById("MY" + i).innerHTML,
fields: ['name', 'rating', 'formatted_phone_number', 'geometry', 'reviews', 'photos'],
};
id_set.push("MY" + i); // this ensures array is populated (in proper order, b/c it tracks the execution of the for loop) for use in callback before callback is called (since getDetails() is async)
service[i] = new google.maps.places.PlacesService(document.createElement('div'));
service[i].getDetails(request[i], function(place, status) {
gp_callback(place, status);
});
}
UPDATE: More scalable and elegant answer after I had a little more time to think about it.
<div id="MY0" class="gp_container">ChIJaZ6Hg4iAhYARxTsHnDFJ9zE</div>
<div id="MY1" class="gp_container">ChIJT9e323V644kRR6TiEnwcOlA</div>
.
.
.
<div id="MYN" class="gp_container">fvbfsvkjfbvkfvb</div> // the nth div
<script>
function populate_container(place, status, container_id) {
var el = document.getElementById(container_id);
if (status == google.maps.places.PlacesServiceStatus.OK) {
el.innerHTML = "<b>" + place.name + "</b><br>" + place.rating + "<br>" + place.reviews[1].author_name + "<br>" + place.reviews[1].rating + "<br>" + place.reviews[1].text + "<br><img src='" + place.photos[0].getUrl({'maxWidth': 250, 'maxHeight': 250}) + "'>";
}
}
function call_service(id_request_map) {
var i, container_id, request,
service_call = function(container_id, request) {
var service = new google.maps.places.PlacesService(document.createElement('div'));
service.getDetails(request, function(place, status) {
populate_container(place, status, container_id);
});
};
for(i in id_request_map) {
service_call(i, id_request_map[i]);
}
}
$(document).ready(function() {
var request, container_id,
id_request_map = {},
container_length = document.getElementsByClassName("gp_container").length,
i = 0;
for (; i < container_length; i++) {
container_id = "MY" + i;
request = {
placeId: document.getElementById(container_id).innerHTML,
fields: ['name', 'rating', 'formatted_phone_number', 'geometry', 'reviews', 'photos'],
};
id_request_map[container_id] = request; // build the association map
}
call_service(id_request_map);
});
</script>
I created a function for reusing the splice feature for javascript arrays however, after I run it once, it cannot be reused.
var removePerson = function(d, person_id) {
var person_index = d.findIndex(i => i.id == person_id);
d.splice(person_index, 1);
return d;
};
I am not getting console errors. I do not know how to debug it. Here is my JSFiddle.
If you run the example, you will see you can remove any 1 person from the list, but when you try to remove either of the remaining 2, nothing happens (e.g. console errors, console response). Any idea how I can support reuse for my removePerson() function?
Your solution doesn't work because of how your populateList works.
In your populateList, you have a line:
$('#load').empty();
This line empties the table and removes the buttons attached with click event listener.
Then, you add completely new button.delete, which aren't attached with any event listener.
To solve this, you can put your .on() into populateList function.
var populateList = function(d) {
$("#load").empty();
var new_rows;
for(var i = 0; i < d.length; i++) {
new_rows += "<tr>" +
"<td>" + d[i].id + "</td>" +
"<td>" + d[i].name + "</td>" +
"<td>" + d[i].phone + "</td>" +
"<td><button class='delete' data-id='" + d[i].id + "'>delete</button></td>" +
"</tr>";
}
$("#load").append(new_rows);
// delete event
$(".delete").on("click", function() {
var delete_sel = $(this).attr("data-id");
populateList(removePerson(d, delete_sel));
});
};
Here's a working jsFiddle.
Alternatively, you can use solution from this answer (which is a cleaner solution imo).
$("table").on("click",".delete", function() {
var delete_sel = $(this).attr("data-id");
populateList(removePerson(data, delete_sel));
});
More explanation on why his answer works on jQuery documentation (Look at the selector parameter).
Try this code:JSFiddle
or the code snippet:
var removePerson = function(d, person_id) {
var person_index = d.findIndex(i => i.id == person_id);
d.splice(person_index, 1);
return d;
};
var populateList = function(d) {
$("#load").empty();
var new_rows;
for(var i = 0; i < d.length; i++) {
new_rows += "<tr>" +
"<td>" + d[i].id + "</td>" +
"<td>" + d[i].name + "</td>" +
"<td>" + d[i].phone + "</td>" +
"<td><button class='delete' data-id='" + d[i].id + "'>delete</button></td>" +
"</tr>";
}
$("#load").append(new_rows);
};
$(document).ready( function() {
// initial list
var data = [
{
"id": 1001,
"name": "Andy Roy",
"phone": "555-555-5551"
},
{
"id": 1002,
"name": "Bob Dillon",
"phone": "555-555-5552"
},
{
"id": 1003,
"name": "Carl Sagan",
"phone": "555-555-5553"
}
];
//initial populate list
populateList(data);
// delete event
$("table").on("click",".delete", function() {
var delete_sel = $(this).attr("data-id");
populateList(removePerson(data, delete_sel));
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table border>
<thead>
<tr><th>ID</th><th>Name</th><th>Phone</th></tr>
</thead>
<tbody id="load"></tbody>
</table>
$("table").on("click",".delete", function() {
var delete_sel = $(this).attr("data-id");
populateList(removePerson(data, delete_sel));
});
I've a sample JSON as given below:
[
{
"componentid": 4,
"displayImageUrl": "https://via.placeholder.com/350x200",
"title": "theme",
"shortdesc": "to set theme for different applications"
},
{
"componentid": 7,
"displayImageUrl": "https://via.placeholder.com/350x200",
"title": "preferences",
"shortdesc": "preferences screen for apps"
}
]
And I prepared a HTML content based on that data. Code is given below:
function prepareTopComponentList(data) {
var preparedHtml = "";
for (var count = 0; count < data.length; count++) {
preparedHtml += "<div class=\"col-lg-4\" style=\"margin-top: 20px\">\n" +
" <div class=\"card wow fadeIn\" data-wow-delay=\"0.2s\">\n" +
" <img class=\"img-fluid\" src=\"";
preparedHtml += data[0].displayImageUrl;
preparedHtml += "\" alt=\"N/A\">\n" +
" <div class=\"card-body\">\n" +
" <h4 class=\"card-title\">";
preparedHtml += data[0].title;
preparedHtml += "</h4>\n" +
" <p class=\"card-text\">";
preparedHtml += data[0].shortdesc;
preparedHtml += "</p>\n" +
" <a onclick = \"redirect(this, this);\" href='#' class=\"btn btn-info\" id=\"";
preparedHtml += "component_desc_" + data[count].componentid;
/*I replaced 0 with count so that we can get proper ids*/
//console.log(data[0].componentid);
preparedHtml += "\">Learn more</a>\n" +
" </div>\n" +
"\n" +
" </div>\n" +
" </div>";
}
$("#div_top_component").append(preparedHtml);
}
Now, I'm redirecting to a page based on value of componentid. Redirection function code is given below:
function redirect(element, data) {
// data = prepareTopComponentList(data);
for (var count = 0; count < data.length; count++) {
var url = "inside.dell.com";
var query = data[count].componentid;
var finalUrl = "inside.dell.com/componentid=" + query;
console.log(finalUrl);
}
window.location = "inside.dell.com/componentid=" + query;
}
When I run this code, I get this error:
The requested URL /Reusable Components/pages/inside.dell.com/componentid=undefined was not found on this server.
That error is fine, I don't have any problem with that. However in redirection URL, component id is undefined. How do I make sure that value of component ID gets passed to URL?
In your HTML
"<a onclick = \"redirect(" + data[count].componentid + ");\" href='#' class=\"btn btn-info\" id=\"";
And then your function
function redirect(id) {
window.location = "inside.dell.com/componentid=" + id;
}
The variable query used for windows.location is defined in the for loop block and therefore is undefined outside of it.
Declare your query variable outside of for loop.
function redirect(element, data) {
// data = prepareTopComponentList(data);
var query;
for (var count = 0; count < data.length; count++) {
var url = "inside.dell.com";
query = data[count].componentid;
var finalUrl = "inside.dell.com/componentid=" + query;
console.log(finalUrl);
}
window.location = "inside.dell.com/componentid=" + query;
}
I'm working on a project where I have a json file that contains a list of companies, their phone numbers, addresses, etc. Currently I have it loading a list into a table with no real order. It works to list the companies, and displays their location on a map with a pin. I have it limited to display on 10 as its almost 5000 entries - at least until I get a sort/search function working. The issue I'm having is that the only way I can get the map to load and the pins to work is if I include them as part of the $.getJSON. If I create functions to load outside of this, I'm unable to call the information out of the JSON any longer, but likewise am not able to include the functions inside as they don't work.
Here is the code I'm working with:
$.getJSON('MapDatabroke.json', function (data) {
var output = "<table class = sample>";
tableHeadings = "<thead>" +
"<tr>" +
"<th></th>" +
"<th><u>Name:</u></th>" +
"<th><u>Address:</u></th>" +
"<th><u>City:</u></th>" +
"<th><u>State:</u></th>" +
"<th><u>Phone Number:</u></th>" +
"<th><u>PO:</u></th>" +
"<th><u>Towing:</u></th>" +
"<th><u>Tires:</u></th>" +
"<th><u>MC:</u></th>" +
"<th><u>RoadSvc:</u></th>" +
"<th><u>Notes:</u></th>" +
"</tr>" +
"</thead>";
output += tableHeadings;
for (var i = 0; i < length; i++) {
var lat = data[i]["Lat"];
var lon = data[i]["Lon"];
var dist = 41.5;
if (lat < dist) {
output += "<tr>";
if (data[i]["Website"] == '---') {
output += "<td>" + ' ' + "</td>";
} else {
output += "<td>W</td>";
}
output += "<td>" + i + data[i]["Business Name"] + "</td>" + "<td>" + data[i]["Address"] + "</td>" + "<td>" + data[i]["City"] + "</td>" + "<td>" + data[i]["StateListing"] + "</td>" + "<td>" + data[i]["Phone"] + "</td>";
if (data[i]["PO"] == 'FALSE') {
output += "<td>" + data[i]["Notes"] + "</td>";
output += "</tr>";
}
}
output += "</table>";
document.getElementById("placeholder").innerHTML = output;
});
function GetMap() {
var map = null;
var pinInfobox = null;
// Initialize the map
var map = new Microsoft.Maps.Map(document.getElementById("mapDiv"), {
credentials: "AqtpRoPv2sfmrgf9VhyvDV8hCOVGPJi0-9heYhxmB-WU24OzpTIOIR0-C4fD0jc-",
center: new Microsoft.Maps.Location(45.5, -122.5),
zoom: 7
});
// Creates a collection to store multiple pins
var pins = new Microsoft.Maps.EntityCollection();
// Create Pins
for (var i = 0; i < length; i++) {
//pushpin location
var position = new Microsoft.Maps.Location(data[i]["Lat"], data[i]["Lon"]);
//Create the pin
var pin = new Microsoft.Maps.Pushpin(position);
//add pin to collection
pins.push(pin);
}
//add pins
map.entities.push(pins);
//create the info box
pinInfobox = new Microsoft.Maps.Infobox(pin.getLocation(), {
title: 'My Pushpin',
description: 'It works!',
visible: false,
offset: new Microsoft.Maps.Point(0, 15)
});
//add click event
Microsoft.Maps.Events.addHandler(pin, 'click', displayInfobox);
//hide box on map move
Microsoft.Maps.Events.addHandler(map, 'viewchange', hideInfobox);
}
function displayInfobox(e) {
pinInfobox.setOptions({
visible: true
});
}
function hideInfobox(e) {
pinInfobox.setOptions({
visible: false
});
}
The way it shows above, doesn't work. It will load the map, but will not load the information as well as give me a error of data not defined w/in the create pin. Any ideas how to assign the information so that way I can use it in functions later on?
Try asigning a variable to the $.getJSON (var JSONData = $.getJSON...). In that way you can have the data outside the query.
On the other hand, you could also use $.ajax to do the query. It's just another way of doing things, a little more "tweakable". This example have some error handlers that will help you identify the problem.
Something like this:
var JSONdata = $.ajax({
type : "GET",
url : "MapDatabroke.json",
cache : false,
dataType: "json", // Use when retrieving
success: function(data) {
// do something here after retrieving the "data".
},
error: function (xhr, ajaxOptions, thrownError) {
//Add these parameters to display the required response
console.log( 'xhr.status: ' + xhr.status );
console.log( 'thrownError: ' + thrownError );
}
});
Also, check your json data (http://jsonlint.com/). Sometimes there is a comma where should not, or a missing bracket.
I've been trying to get some AJAX code that runs fine in FireFox to run in IE.
I'm running into some trouble with updating some tables in the script though. I've seen numerous other people have similar issues, but none of the solutions they've found have worked for me. The problem occurs first on the line
qe3Table.innerHTML =
"<tr>\n" +
" <th>Name</th>\n" +
" <th>Status</th>\n" +
" <th>View Status</th>\n" +
"</tr>\n";
Where I'm getting the error "'null' is null or not an object"
I'm pretty sure that all of my other errors are of the same type as this one, my AJAX script and some accompanying javascript is below.
<script type="text/javascript">
<!--
//obtains the box address for a QE3 on the system for the given index
function getQE3BoxAddressHash(index)
{
var retVal = 0x00000100; //initial value for QE3 boxes
retVal |= (index & 0x000000FF);
return retVal;
}
//obtains the box address for a QED on the system for the given index
function getQEDBoxAddressHash(index)
{
var retVal = 0x00001300; //initial value for QED boxes
retVal |= ((index & 0x0000000F) << 4);
retVal |= ((index & 0x000000F0) >> 4);
return retVal;
}
-->
</script>
<script type="text/javascript">
<!--
var textSocket;
function fillTables()
{
if(textSocket.readyState != 4)
return;
var qe3Table = document.getElementById("QE3_TABLE");
var qedTable = document.getElementById("QED_TABLE");
var rawData = textSocket.responseText.split("::::");
var qe3Data = new Array();
var qedData = new Array();
var qe3Index = 0;
var qedIndex = 0;
for(var item in rawData)
{
if(rawData[item].indexOf("QA") != -1)
{
qe3Data[qe3Index++] = rawData[item];
}
else if(rawData[item].indexOf("QED") != -1)
{
qedData[qedIndex++] = rawData[item];
}
}
qe3Table.innerHTML =
"<tr>\n" +
" <th>Name</th>\n" +
" <th>Status</th>\n" +
" <th>View Status</th>\n" +
"</tr>\n";
qedTable.innerHTML =
"<tr>\n" +
" <th>Name</th>\n" +
" <th>Status</th>\n" +
" <th>View Status</th>\n" +
"</tr>\n";
for(var value in qe3Data)
{
var components = qe3Data[value].split("-");
if(components.length != 3)
continue;
qe3Table.innerHTML +=
"<tr>\n" +
" <td>" + components[0] + "-" + components[1] +"</td>\n" +
" <td>" +
((components[2].toUpperCase() === "ONLINE")?
"<font color=\"green\"><b>ONLINE</b></font>":
"<font color=\"red\"><b>OFFLINE</b></font>")+
"</td>\n" +
" <td>\n <input type=\"button\" onclick=\"window.location='system_status.php?boxAddress=" + getQE3BoxAddressHash(value).toString(16) + "'\" value='View Status for " + components[0] + "-" + components[1] +"'></input> </td>\n" +
"</tr>\n";
}
for(var value in qedData)
{
var components = qedData[value].split("-");
if(components.length != 3)
continue;
qedTable.innerHTML +=
"<tr>\n" +
" <td>" + components[0] + "-" + components[1] +"</td>\n" +
" <td>" +
((components[2].toUpperCase() === "ONLINE")?
"<font color=\"green\"><b>ONLINE</b></font>":
"<font color=\"red\"><b>OFFLINE</b></font>")+
"</td>\n" +
" <td>\n <input type=\"button\" onclick=\"window.location='system_status.php?boxAddress=" + getQEDBoxAddressHash(value).toString(16) + "'\" value='View Status for " + components[0] + "-" + components[1] +"'></input> </td>\n" +
"</tr>\n";
}
}
function initAjax()
{
try
{
// Opera 8.0+, Firefox, Safari
textSocket = new XMLHttpRequest();
}
catch (e)
{
// Internet Explorer Browsers
try
{
textSocket = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e)
{
try
{
textSocket = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e)
{
// Something went wrong
alert("A browser error occurred.");
return false;
}
}
}
textSocket.onreadystatechange=fillTables
}
function reloadTables()
{
textSocket.open("GET","ajax_scripts/get_connected_boxes.php",true);
textSocket.send(null);
}
function init()
{
initAjax();
reloadTables();
}
window.onload=init();
-->
</script>
The problem is probably with:
var qe3Table = document.getElementById("QE3_TABLE");
If you're running this script before the body is loaded, that won't exist. Check to see if that variable has anything in it.
I Tried both of your guys' fixes but they didn't seem to help. In the end, I converted all calls of the form:
qe3TableNew.innerHTML = ("<tr>\n" +" <th>Name</th>\n" +" <th>Status</th>\n" +" <th>View Status</th>\n" +"</tr>\n");
to
var row;
var cell;
var text;
var font;
row = document.createElement("tr");
qe3TableNew.appendChild(row);
cell = document.createElement("th");
row.appendChild(cell);
text = document.createTextNode("Name");
cell.appendChild(text);
cell = document.createElement("th");
row.appendChild(cell);
text = document.createTextNode("Status");
cell.appendChild(text);
cell = document.createElement("th");
row.appendChild(cell);
text = document.createTextNode("View Status");
cell.appendChild(text);
This seemed to solve it, so I believe it has to do with IE's inability to handle changes to innerHTML.
Thanks for the help guys.
At least one issue (which could produce the above symptoms) is the line:
window.onload=init();
Hint: the () operator executes the function immediately and evaluates to the return value. This in turn may allow the XHR handler (in certain situations) to fire at a time when the DOM may not be ready.
Happy coding.