Creating geolocation question templates
You could create question templates that use HTML 5 geolocation features that are native to the web browser, but still fall back to older, less accurate methods when gathering location information when a respondent’s device does not natively support geolocation.
The respondent’s privacy must be considered when creating these controls. Even though there are ways of obtaining general location information, you must respect your respondents’ privacy. You can eliminate much of the respondent's manual data input by automatically collecting information whenever possible (as long as the respondents are comfortable with this). Many of the desktop and mobile browsers can provide latitude and longitude information to requesting web pages. Depending on the level of accuracy provided by these devices, Country, City, Zip Code, and even address can be looked up in web services provided by various 3rd party providers.
A JavaScript file hosted by the 3rd party is typically required in order to include a 3rd party web service in a question template.
This is a potential security problem. Only use trusted 3rd party providers whose privacy policies are compatible with your own. Also, by using JavaScript hosted by a 3rd party, you will potentially open up all the data on that page to the 3rd party, which potentially allows the 3rd party to inject content into your page.
A basic geolocation template could resemble the following:
<mrSubTemplate>
<!—Add your 3rd party JavaScript -->
<mrRef RefType="script" RefPosition="head" type="text/javascript" src="http://EXAMPLE3RDPartySite.com/geolocationAPI.js" >//Script
</mrRef>
<!—Add your own control JavaScript to be added once after the body tag-->
<mrRef RefType="script" RefPosition="bottom" type="text/javascript" src="MyCustomGeolocationCode.js" defer="defer">// Script</mrRef>
<mrRef RefType="link" RefPosition="head" type="text/css" rel="stylesheet" MyCustomGeolocationCode.css"/>
<div class="city">
<mrData QuestionElement="Banner" />
<mrData QuestionElement="Error" />
<mrData QuestionElement="Label" />
<mrData QuestionElement="Controls"/>
</div>
</mrSubTemplate>
This template assumes you have the respondent’s permission to use their location data before the page is loaded. Alternatively, you could first obtain permission and then add the 3rd party JavaScript after the user has given permission to look up their data. In your own control JavaScript, you could add the following code to add the 3rd party script file:
//permissionToUseGeoLocation was set earlier
// via user interaction...
(function() {
If (permisionToUseGeoLocation == true) {
var hd = document.getElementsByTagName("head")[0];
var GeoLocationServiceScript = document.createElement(“script”);
GeoLocationServiceScript.setAttribute("type”, "text/JavaScript");
GeoLocationServiceScript.setAttribute("src", "http://EXAMPLE3RDPartySite.com/GeolocationServiceURLProvidedBy3rdParty.js");
hd.appendChild(GeoLocationServiceScript);
})();
Some 3rd party APIs expect the latitude and longitude information, and will therefore requires the information to be included in the request URL query string. Others 3rd party APIs might require the latitude and longitude information as function call parameters. Still others might not require latitude and longitude at all, but guess the respondent's location based on the respondent’s browser IP address when it makes the script file request. The last method is the least accurate as it is generally be correct within the city, and assumes the respondent is not using a virtual private network. It does however have the advantage of being usable with older web browsers. For more information, see the 3rd party API’s documentation.
After you have connected to the 3rd party geolocation service, you have access to metadata regarding the user coordinates by using:
▪JavaScript methods: (getTheCity(), getTheAddress(), and so on
▪JSON: location.city, location.address
▪XML: <location city="" address="" zip="" .../>
One of the more flexible methods for collecting address information is to create a separate question template for each piece of location information and then add all the templates to a page. For example:
▪Country – Geolocation-Country.htm
▪City – Geolocation-City.htm
▪Postal Code – Geolocation-PostalCode.htm
▪Address – Geolocation-Address.htm
Each of the question templates would be identical except the container class name around the control:
<mrSubTemplate>
<mrRef RefType="script" RefPosition="head" type="text/javascript" src="http://EXAMPLE3RDPartySite.com/geolocationAPI.js" >//Script
</mrRef>
<mrRef RefType="script" RefPosition="bottom" type="text/javascript" src="MyCustomGeolocationCode.js" defer="defer">// Script</mrRef>
<mrRef RefType="link" RefPosition="head" type="text/css" rel="stylesheet" MyCustomGeolocationCode.css"/>
<div class="city">
<mrData QuestionElement="Banner"/>
<mrData QuestionElement="Error"/>
<mrData QuestionElement="Label"/>
<mrData QuestionElement="Controls"/>
</div>
</mrSubTemplate>
Doing this ensures the following:
▪The 3rd party script is only included once, regardless of whether it was included with an mrRef tag or programmatically after page load.
▪The custom geolocation JavaScript and CSS is only included once.
▪You do not need to retrieve every piece of information from the service, place the information into a single textarea and then parse the information via mrScript. You can simply write one JavaScript file that fills the controls that are present on the page.
Since the custom JavaScript was added to the bottom of the page, you already know that all the dom elements have been loaded into the browser. You can therefore start looking for the applicable controls, since <div> elements were used with class names. For example:
/*NOTE: the value property for each item in the locationObject is fictional, and for demonstration purposes only. For a real API,
use the function or data item supplied by the 3rd party web service you are using */
var locationObject = {
country : {input:null, label:null, value:getCountry()},
city : {input:null, label:null, value:getCity()},
region : {input:null, label:null, value:getRegioun()}
latitude : {input:null, label:null, value:getLatitude()},
longitude : {input:null, label:null, value:getLongitude()},
postalCode : {input:null, label:null, value:getZip()}
};
var divs = document.getElementsByTagName(“div”);
for (var i=0;i<divs.length;i++) {
var className = divs[div].className;
if (className in locationObject) {
/*since text controls could be either inputs of type text or textarea, check both, furthermore controls for do not
know, no answer, and refused to answer could be added to each control
Check for inputs first, then look for text areas */
inputs = divs[div].getElementsByTagName("input");
if (inputs.length != 0) {
for (var i= inputs.length-1;i>-1;i--) {
if (inputs[i].getAttribute("type") != "text") {
//If its not type text, get rid of it and move to the next input.
inputs.splice(i,1);
}
}
}
if (inputs.length == 0) {
inputs = divs[div].getElementsByTagName("textarea");
}
inputs[0].value = locationObject[className].value;
}
}
This example assumes you are using an API that retrieves latitude and longitude based on the respondent's IP address. You can write your own code to obtain latitude and longitude from newer browsers that support geolocation, and use these as arguments in other APIs. To get latitude and longitude information from the browser, you could use the following code:
Var maxTime=10000; //max time out (10 seconds)
var timeout = 10000; //current timeout value (10 seconds) – used for consistency across browsers for a backup timeout method
var doneWaiting = false;
if (!navigator.geolocation) {
//navigator does not support geolocation
geoFail();
return;
}
else {
//getCurrentPosition(successFunction, failureFunction, timeoutArgument)
navigator.geolocation.getCurrentPosition(geoSuccess, geoFail, {timeout:maxTime});
testTimeout();
}
function geoSuccess(pos) {
doneWaiting = true;
//Now fill in the controls programmatically for the respondent
}
function geoFail() {
doneWaiting = true;
//there is nothing else to do, other than inform the respondent that they must manually enter their location data
}
//this function will be used to force timeout across browsers. Currently the timeout argument is inconsistent.
function testTimeout() {
if (timeout <= 0) {
//Timeout = 3
geoFail();
return;
}
else if (doneWaiting == true) {
return;
}
else {
timeout -= 1000;
setTimeout(testTimeout, 1000);
}
}
See also