2
<button type = "button" onclick="loadDoc">IMAGE TEST</button>
<p id = "demo"></p>
<script>
    function loadDoc(){
        for (let item in Response.items) {
             for (let camera in item.cameras) {
                  const imageUrl = camera.image; 
                  document.getElementById("demo").innerHTML=camera.image;
             }
        }
        xhr.open("GET","https://api.data.gov.sg/v1/transport/traffic-images",true);
        xhr.send();
    };
</script>

I want to extract the image from the API provided by i wasn't able to show it in the html file

Output shows:

{"items":[{"timestamp":"2018-12-20T21:59:41+08:00","cameras":[{"timestamp":"2018-12-20T21:59:21+08:00","image":"https://images.data.gov.sg/api/traffic-images/2018/12/05cd4190-b4e4-430c-b1c1-7047972c09fe.jpg","location":{"latitude":1.27414394350065,"longitude":103.851316802547},"camera_id":"1501","image_metadata":{"height":240,"width":320,"md5":"7bcfabb38b60a18f1380bffca095d6c0"}},{"timestamp":"2018-12-20T21:59:21+08:00","image":"https://images.data.gov.sg/api/traffic-images/2018/12/06c05520-98b6-4086-b691-1bb54ed90482.jpg","location":{"latitude":1.27135090682664,"longitude":103.861828440597},"camera_id":"1502","image_metadata":.....
Julian
  • 1,592
  • 1
  • 13
  • 33
Opkko Lim
  • 43
  • 1
  • 9
  • well it is not an image tag so..... How would you make an image in HTML? Same rules apply.... – epascarello Dec 20 '18 at 14:33
  • hi epascarello, in html we do but in this case do i do document.getElementById("demo").img?? quite confused with the syntax here – Opkko Lim Dec 20 '18 at 14:46

4 Answers4

0

There are a few problems with the code you presented:

  1. You're using document.getElementById("demo") inside a for loop. You're essentially querying the same element again and again and always getting the same result. If you only need to update a single element, just place it outside the for loop, save it to a variable and use it inside the loop. If you're trying to update multiple elements, you should probably be using dynamic class names or query elements differently.
  2. For images you should not be using innerHTML. Instead, you can either use and HTML <img> tag and update the src attribute, or you could use a generic HTML tag like <div> or <span> and apply a css rule of background-image with the url of the image.
  3. You're not using xhr correctly. You're trying to access the response before it is even sent. Check out an example of how to instantiate and use an xhr object: Sending an XMLHttpRequest
Shahar
  • 2,269
  • 1
  • 27
  • 34
  • As the url for the images are dynamic because they are live feeds how do I actually draw on them? Sorry if my question seems very amateur because I am pretty new to the language.. it is from this api called https://api.data.gov.sg/v1/transport/traffic-images – Opkko Lim Dec 20 '18 at 14:44
  • You can assign each url to an img.src by accessing the src attribute like this: `document.getElementById("myImage").setAttribute("src", dynamicUrl);` remember that since you have multiple URLs, each should be assign to a different img element, so using ID is not a good option (this is just for example). – Shahar Dec 23 '18 at 08:40
  • Meaning dynamicUrl will be a variable which contains the different Urls? – Opkko Lim Dec 25 '18 at 08:18
  • Yes. To be more precise, dynamicUrl is a variable which can be a different each time. I assume it will be inside a loop which checks for the response you got from the server and then assign each url to a different img tag in the DOM (even better - create the img elements only when needed and finally attach it to the DOM). – Shahar Dec 25 '18 at 11:48
0

camera.image resolves to the string "https://images.data.gov.sg/api/traffic-images/2018/12/05cd4190-b4e4-430c-b1c1-7047972c09fe.jpg", so document.getElementById("demo").innerHTML=camera.image simply sets the contents of your <p id="demo"></p> element to this string:

<p id="demo">
    https://images.data.gov.sg/api/traffic-images/2018/12/05cd4190-b4e4-430c-b1c1-7047972c09fe.jpg
</p>

It should be clear why this isn't working: you're putting text in a <p> element and expecting it to show an image. That won't work. You need to use <img> and set its src attribute to this URL for your code to work the way you want:

HTML:

<img id="demo" src="">

Javascript:

$('#demo').attr('src', camera.image);
Bucket
  • 7,415
  • 9
  • 35
  • 45
  • in this case, for src = "" what do i put in " " ? do i add the api link? – Opkko Lim Dec 20 '18 at 15:12
  • @OpkkoLim set the `src` attribute to the URL to your image. The empty string is a placeholder for you to insert the correct value with your Javascript. My answer outlines this. – Bucket Dec 20 '18 at 18:49
  • hi! however the url to my image is always different because this is a live feed!! So in this scenario how do i know what to put in the placeholder? – Opkko Lim Dec 21 '18 at 01:58
  • @OpkkoLim I assume it should be whatever `camera.image` resolves to. – Bucket Dec 21 '18 at 15:21
  • Noted! Let me try the suggestion Bucket – Opkko Lim Dec 25 '18 at 08:19
0

How does this look? Of course you'd have to implement the AJAX call yourself and whatnot, but you get the idea?

Edit

Have you thoguht about looking at the fetch API? If you want to use xhr, that's fair enough, but fetch is just a newer and nicer way to implement xhr.

const url = 'https://api.data.gov.sg/v1/transport/traffic-images';
let appRoot, btn;

// Go get the data, return a promise. 
const getData = () => fetch(url).then(imgs => imgs.json());

// Output the image. 
const processImage = img => {
  const el = document.createElement("img");
  el.setAttribute("src", img.image);
  appRoot.append(el);
};

// Iterate over the data from the getData function.
const process = data => {
  appRoot.innerHTML = "";
  data.items.forEach(o => o.cameras.forEach(img => processImage(img)));
};

// Bind the event hanlder to the event. 
const bindEvents = () => btn.onclick = () => getData().then(process);

// A simple function to launch everything.
const launch = () => {
  console.log('Starting...');
  btn = document.getElementById("loadDoc");
  appRoot = document.getElementById("demo");
  bindEvents();
};

// Start the app.
launch();
<button type="button" id="loadDoc">IMAGE TEST</button>
<div id="demo"></div>
  • Hi jack! can i ask what does this do? const getData = () => I am not sure of how this operator works so.. getData gets my data and i intialize a new object Promise which returns the data.items? sorry cause i am really really new to the language!! Really appreciate your help! – Opkko Lim Dec 20 '18 at 14:54
  • It's an [arrow function](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions). Be aware that they do not work in IE at all. – Rory McCrossan Dec 20 '18 at 14:57
  • Hi @OpkkoLim that syntax is the ES6 way to write what's called a lambda function... But yes, essentially the ```Promise``` object is a way of saying ```do this until that has finished, once it has, do something else```, it's a clean way to implement what's known as asynchronous code, slowly becoming an industry standard, if not it already is. –  Dec 20 '18 at 14:58
  • @RoryMcCrossan you can always use some transpiler to get around that?! :) –  Dec 20 '18 at 14:58
  • @JackJones now i know you can actually tag people in comments hahaha okay so what is a transpiler? also this lambda function is also really new to me.. is this promise object something very common? – Opkko Lim Dec 20 '18 at 15:01
  • @RoryMcCrossan i see that link is tagged to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions is this a good place for expanding my js knowledge compared to w3schools? – Opkko Lim Dec 20 '18 at 15:05
  • Yes, absolutely. W3Schools are very unreliable and should never be used. MDN is the best resource for JS. – Rory McCrossan Dec 20 '18 at 15:09
  • @OpkkoLim it's simply a way in which you can write more modern JavaScript, then 'compile' it down to JavaScript that will work in older browsers, often used to support IE. Promises, yes, they're incredibly common these days... I'm surprised you've not stumbled onto promises prior to now. –  Dec 20 '18 at 15:13
  • @RoryMcCrossan thanks!! let me check out the website and strengthen my basics. JackJones I am really new to this language been like only maximum a week since i picked this up! so everyone's feedback here is really helpful and i'm absorbing a bunch of knowledge! Do you guys have like telegram or something where i can contact you ? – Opkko Lim Dec 20 '18 at 15:17
  • @OpkkoLim That's fair enough, as Rory has said, MDN is possibly the best resources out there to aid learning JavaScript... Sorry if this implementation is a little advance or not... I hope I've only helped and not made your learnign experience any more difficult! –  Dec 20 '18 at 15:20
  • @JackJones it is okay every comment here is beyond what i currently know so i am scrambling while googling and reading and testing how all these works! really grateful to all the replies and answers here! My idea is the more you know the more you've yet to know! – Opkko Lim Dec 20 '18 at 15:24
  • @JackJones i actually tried fetch awhile back but it wasn't documented in w3schools or at least I didn't come across it so I couldn't make it work – Opkko Lim Dec 20 '18 at 15:29
  • @OpkkoLim I actually really like that philosophy... But anyway, if you want to take a look at the link I've provided in my answer above, it'll take you right to the MDN docs page for fetch, it's prety great! –  Dec 20 '18 at 15:42
  • Thank you so much jack! I will take a look hahaha – Opkko Lim Dec 21 '18 at 01:53
0

There are several issues in your code:

  • You don't define xhr at all. You need to create an XMLHttpRequest object to call send() on.
  • You don't define an onload handler for the XHR to execute when the request completes, your logic is simply placed within the root of the function so will just cause errors.
  • You're using a for..in loop in an array. Use a plain for loop, or better still, forEach()
  • You're updating the text of the #demo element not setting the src of an img
  • You need to create a new img element for every item in the returned JSON and then add that to the DOM, presumably within the #demo element
  • You're using an inline event handler, onclick, which should be avoided. Use an unobtrusive event handler instead, eg. addEventListener().

With all those points in mind, try this:

document.querySelector('#load').addEventListener('click', function() {
  var div = document.getElementById('demo');
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "https://api.data.gov.sg/v1/transport/traffic-images", true);
  xhr.onload = function() {
    var response = JSON.parse(this.responseText);
    response.items.forEach(function(item) {
      item.cameras.forEach(function(camera) {
        var img = document.createElement('img');
        img.src = camera.image;
        div.appendChild(img);
        
        var p = document.createElement('p');
        p.innerText = camera.location.latitude + ', ' + camera.location.longitude;
        div.appendChild(p);
      });
    });
  };
  xhr.send();
});
<button type="button" id="load">IMAGE TEST</button>
<p id="demo"></p>
Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
  • Hi Rory!! From your code I can see that you made used of jQuery, do let me read up abit on jQuery first before i can understand fully what has been done! – Opkko Lim Dec 20 '18 at 15:00
  • There's no jQuery in this answer, it's all plain JS. I can provide a jQuery alternative if you'd prefer. – Rory McCrossan Dec 20 '18 at 15:02
  • it is okay I still haven't read up on jQuery yet.. for now I am studying on your code and trying to write my own version to see if it works. however just a quick question because the code displays all the images but since this live feed always randomises the locations, what is your logic is displaying them in a fixed manner? – Opkko Lim Dec 20 '18 at 15:08
  • This logic will display them in the order they are returned in the JSON from the API call. – Rory McCrossan Dec 20 '18 at 15:17
  • because the api call randomises the camera ids and locations so each time it runs, the order changes so I am wondering if i can display them in a fixed order if i want to – Opkko Lim Dec 20 '18 at 15:19
  • You can do, you'd need to `sort()` the `cameras` array by the `timestamp` before you loop through it. See [this question](https://stackoverflow.com/questions/41506531/order-array-of-objects-by-date) for details of how to do that – Rory McCrossan Dec 20 '18 at 15:21
  • is there a need to sort by the timestamp? why can't i just sort by camera id? – Opkko Lim Dec 20 '18 at 15:22
  • Sure, you could do that too. I just assumed you wanted a date-ordered list – Rory McCrossan Dec 20 '18 at 15:24
  • AH! no i was just wondering if the timestamp itself is very important hmm cause I've been thinking how to display it in a fixed manner afterwards I would need image processing or machine learning to display congestion.. – Opkko Lim Dec 20 '18 at 15:26
  • One last question Rory! If i decided to show the location is it complicated? of where the camera is do i need another map api or something? – Opkko Lim Dec 20 '18 at 15:37
  • Not complicated at all. You've already got a reference to the location data through the `camera` object, and you can already see the logic to create and append elements. I've updated the answer to show you exactly how this can be done. – Rory McCrossan Dec 20 '18 at 15:43
  • But what if just what if i want to pinpoint the location to words for example Buckhem Street blah blah. Instead of the numerical value of the location? things get complicated this way correct? Sorry I am just asking ! – Opkko Lim Dec 21 '18 at 01:54
  • Yes, that gets complicated. It's called reverse geolocation. It's possible by using the Google Maps API, though. It just takes some configuration, a reference to the Google SDK and some more AJAX requests. See this guide for a demo: https://developers.google.com/maps/documentation/javascript/examples/geocoding-reverse – Rory McCrossan Dec 21 '18 at 07:50
  • okay! can i ask you if i want to do image processing, i have to use backend? like node js? – Opkko Lim Dec 23 '18 at 05:40
  • Yes, that's correct - backend libraries are far better suited to image manipulation. IVF not used node though, only C# and PHP – Rory McCrossan Dec 23 '18 at 09:07
  • What is IVF though? – Opkko Lim Dec 24 '18 at 01:51
  • Sorry, that should say "I've" – Rory McCrossan Dec 24 '18 at 08:02
  • OH now it makes sense hahaha can I ask you something to do with logical thinking? I am thinking since i need to process the image. which is gotten from the parsed json file from the server/url do i need to save this and then process it each time? Or can the processing work by taking it instantly and process without saving the images.? – Opkko Lim Dec 24 '18 at 08:23
  • There's a lot of factors which would affect how you process and serve the image, such as concurrency, scale and persistence. I'd suggest asking a question on the software engineering site. – Rory McCrossan Dec 24 '18 at 08:51
  • Where do i get to the software engineering site? Also great help from you rory! Merry Christmas – Opkko Lim Dec 25 '18 at 06:35