Here is a working proof of concept in vanilla JS that uses the fetch API to retrieve and parse JSON data. Coming from a server-side background, I wanted insight as to where my beginner JS skills could be improved. Namely, with looping through results, error detection/display, and escaping 'bad data' from third parties before displaying on screen.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Javascript Experiments - fetch(ing) Data from the Oodle API</title>
<style>body {font-family:'Oxygen', sans-serif;font-size:14px;line-height:18px;color:#252525;font-weight:400;}</style>
<body>
<h1>Javascript Experiments - fetch(ing) Data from the Oodle API</h1>
<form>
<input id="st" type="text">
<select id="numrequests">
<option value="5" selected>5</option>
<option value="10">10</option>
<option value="15">15</option>
<option value="20">20</option>
<option value="25">25</option>
</select>
<input type="submit" value="Search It Up!">
</form>
<p id="result"></p>
<script>
document.querySelector('form').onsubmit = function(e) {
e.preventDefault()
let oodleurl, txt='', noimage = '<img src="img/noimage.gif">'
let searchterm = document.getElementById('st').value.trim()
let sel = document.getElementById('numrequests')
let numreq = sel.options[sel.selectedIndex].value
let p = {
category: 'vehicle/parts',
oodlepartnerid: 'TEST',
oodleexclude: 'ebay',
searchterm: searchterm,
numreq: numreq
};
oodleurl = 'https://api.oodle.com/api/v2/listings?key=' + p.oodlepartnerid + '®ion=usa&q=' + encodeURIComponent(p.searchterm) + '&category=' + p.category + '&num=' + p.numreq + '&sort=ctime_reverse&exclude_sources=' + p.oodleexclude + '&format=json&jsoncallback=none'
fetch(oodleurl)
.then(resp => {
if (resp.status === 200) {
return resp.json()
} else {
throw new Error('There was a problem with the API request.')
}
})
.then(resp => {
if (Array.isArray(resp.listings) && resp.listings.length) {
resp.listings.forEach(function(v, i) {
if (Array.isArray(v.images) && v.images.length) {
txt += (v.images[0].hasOwnProperty('src') ? '<img src="' + v.images[0].src + '"><br>' : noimage + '<br>')
} else {
txt += noimage + '<br>'
}
txt += 'TITLE: ' + (v.hasOwnProperty('title') && v.hasOwnProperty('url') ? '<a href="' + escapeOutput(v.url) + '" target="_blank">' + escapeOutput(v.title) + '</a>' : 'n/a') + '<br>'
txt += 'BODY: ' + (v.hasOwnProperty('body') ? escapeOutput(v.body) : 'n/a') + '<br>'
txt += 'CITY: ' + (v.location.hasOwnProperty('name') ? escapeOutput(v.location.name) : 'n/a') + '<br>'
txt += 'STATE: ' + (v.location.hasOwnProperty('state') ? escapeOutput(v.location.state) : 'n/a') + '<br>'
txt += 'COUNTRY: ' + (v.location.hasOwnProperty('country') ? escapeOutput(v.location.country) : 'n/a') + '<br>'
txt += 'PRICE: ' + (v.attributes.hasOwnProperty('price_display') ? escapeOutput(v.attributes.price_display) : 'n/a') + '<br>'
txt += 'POSTED: ' + (v.hasOwnProperty('ctime') ? epochToDate(v.ctime) : 'n/a') + '<br>'
if (i+1 !== resp.listings.length) txt += '<hr>'
})
return document.getElementById('result').innerHTML = txt
//
} else {
return document.getElementById('result').innerHTML = 'There are no results to display.'
}
})
.catch(function(error) {
console.log('error', error)
})
}
function escapeOutput(str) {
return str.replace('&','&').replace('<','<').replace('>','>').replace('"','"').replace("'",''').replace('/','/');
}
function epochToDate(epoch) {
if (epoch < 10000000000) {
epoch *= 1000
var epoch = epoch + (new Date().getTimezoneOffset() * -1)
return new Date(epoch).toLocaleDateString()
}
}
</script>
</body>
</html>