 Hey, everyone. It's Matt again. I'm here to talk a little bit more about the app that I've been building. If you don't already know about the app, you can watch an introduction in the previous video, and there's a link in the description. Now, this is a camera app. And direct access to the camera is a pretty new feature that's available in modern browsers, but not so much in older browsers, and not for people who don't even have a camera. For example, people that are on desktops. So how do we progressively enhance capturing an image? Well, one of the easiest things we could do is take a URL of an image that's actually hosted elsewhere on the web and just fetch that. So if we're taking an image from a URL, the code for that is dead simple. We just need some input to take the URL and then some sort of action, in this case a button, say that we'd like to actually go for the URL, and then we just set the source of an image to be whatever the user typed in. So I have a test image URL here, I paste it in, and click go, and my image is in the page. This is a very, very simple way of doing this. It does have a couple of problems, though. The big one being that if the server that the image is hosted on doesn't support cause headers, or doesn't send the right cause headers, then you won't actually be able to edit this image. You'll still be able to display it in the page, but we're making a photo editing app, essentially. So we need to be able to get at those pixels and transform them in some way. And without cause, that's just not going to happen. You can get around that by using a proxy server. So you run your own server and you fetch all URLs through that. So you make a request to your server with a URL, and then the server goes away and makes the request to the real server, and then the image comes back through that chain. But that's a little bit excessive for what we want to do here. You have to run your own server, which can handle the traffic of all of your users, for something you really only want to be doing inside the browser. So how can we improve on using a URL provided by the user? Well, we could use a file input and let the user just upload the photo directly to us. So it's an input of type file, which tells the browser that it should prompt the user to give us a file. And traditionally, this was so that that file could be uploaded to your server as part of a form. But in modern browsers, we can take the file from the input and use it in the client without actually sending it to the server. Now we can enhance this again. The first enhancement is to say that we accept images. So here we use the accept attribute and we pass in a MIME type, or in this case, a wildcard MIME type. We say image slash star to say, we want the user to give us an image. So the browser can then prompt the user with a file input that only gives them images. So if we see here, when we open this up, this is showing me only the images that are on my desktop. Now the file input has a change event, which we can listen to. And anytime that the user changes what is selected in that file input, this event will get triggered. And here I'm just logging to the console what the output of that is, the files object of our input. So here in our handler, I'm taking e.target, which is the target of the event, which is actually gonna be the file input. And we're looking at this files property. And for a moment, I'm just logging that out to the console and you can see that it is a file list. And we'll learn more all about file lists later on. The other good thing about the accept attribute is that it lets mobile browsers know that they could send us a photo straight from the camera if they wanted to. So when they pop up the chooser, as well as offering the ability to upload a file, they also give the opportunity to say, would you like to take a photo from the camera? This will go straight to the native camera app and allow the user to take a photo there and then, which can then get passed into your app, which is brilliant. This is almost what we want for our own app. There is a final possibility for this, which is we can add another attribute. We can add in an attribute called capture. So we add in capture here. And by default, if you just put in capture, that says that I'd like to go to the camera directly. So on a mobile browser, this will pop up the native camera app without offering the chooser first, which if you really only want photos from a camera is perfect. You skip a step. But there's a little bit of a problem here. You can't feature detect where the capture is going to do this. And this means that on desktop browsers, you're only going to prompt for a file because that's what the file input does. Whereas on mobile browsers, you're only going to prompt for images from the camera. It's not an equal thing between the two. And what if a user on mobile wants to upload a file that's on their SD card, for example? If you are going to use the capture attribute, there is actually a couple of extra things you can do. Is that you can set it to one of two values to say which camera you'd prefer. So you can set it to be user, which says that you want the camera that is facing the user. So this is your selfie camera. Or you can set it to be capture equals environment, which says you want the camera that faces the environment, i.e. the normal camera that faces away from the user. If the device happens to only have one camera, it will still give you the camera that it does have, and this just sets a preference. So the accept header and the capture attribute give us a couple of ways to enhance the experience for mobile users. So let's look at some ways we can enhance things for desktop users as well. One thing that's fairly common to do on desktop that you wouldn't do on mobile is drag and drop. And that's supported by the web as well. So here we set up a target to be dropped onto. The target is just a div element. It could be pretty much any element you like. And we set up this drop event listener so that when something is dropped onto that element, what should we do? Well, we should stop propagation because we're actually handling it in our element here. And we should prevent the default too. If I drag this image just into the browser, not onto the element, it just loads the image into the tab and is no longer on our page. And we don't want that to happen so that as soon as we drop it onto our element, we get the event fired, but then it navigates the image. So we prevent the default behavior as well. Here again, just logging out the results of this. The event here is a drag event which has a data transfer object. Again, we'll talk about that. And that has a files property which is again a file list. So if I drag this onto the element, we can see down here that we get the file list object. Now I have another event listener down here for drag over. And this says what should happen as you're dragging this over the element, what should the behavior be? And here we're saying that we want the effect to be a copy so that when the drag finishes, when that drop happens, we are copying the information from the image into the event. And again, we need stop propagation and prevent the default. Stop the normal browser things happening when we drag that over. And it is the setting the drug drop effect to copy which gives us this plus icon on the mouse. Lastly, another desktop feature is the clipboard API that allows us to copy an image from somewhere from the file system or from another application and just paste it straight into the app. Now in Chrome, you can paste an image or any file into any element on the page. You could set the event listener for the paste event on the document and that would work fine. But not all browsers support this the same way and Microsoft Edge, for example, requires that the element be both selectable and editable. So here I've got a text area which I can paste into. And you can see immediately there's a slight UX problem here. We're not asking the user to type anything in but we've given them a text input. So this makes it slightly awkward to let users know what they should be doing in your app. You can, if you want, take an editable element, put it off screen, and then when someone clicks onto an element that is on screen but not editable, you actually set the focus manually in your code to be the editable element to when the user pastes, it goes to the element that you want. But then you run into some accessibility issues and it's just slightly awkward. For that reason, I'm not going to add paste into my own camera app, but I just wanted to let you know that the option was there. So how do we actually handle a paste event? Very easy. We take whatever element that we're gonna use. In this case, it's our text area and we add an event listener for the paste event. And when that event gets handled, we get the event has a clipboard data property with a files property inside it. And that files property, as we can see, is yet again another file list. And let's just show that this really works. I copy the image and I paste it in. And after a moment, because this is a big file and it is doing a copy, we get a new entry in our console with the filing. And you can see that we get the name and modify date and things like that. So that brings us to the question of, what is a file list object? What is this thing that we've been getting? We've been getting it from the file input, we got it from drag and drop, and then we got it from the clipboard API. So a file list is kind of like an array. It's an object with numeric keys, with each property being a file object. As I say, it's array-like. It doesn't have any of the array methods like forEach or map, but you can create an array by using array.from. So I could say my array is array.from file list. In this case, though, because it's numeric keys, I can still use a normal for loop over it. So that's what I'm gonna do here. As I say, the entries in this file list are file objects. Now a file object is actually exactly the same as a blob. So if you've used a blob before, you know exactly how to use this. The only difference is that a file object, if we look over here, has a couple of extra properties. It has the last modified and it has the name, things that are file type information that you wouldn't have on the blob. And this means that you can use it however you would use a blob. Blobs can be stored in index DB. You can get a URL from them, which means that you can use that URL as the source of an image. You can pass them around to workers and things because they are strict cloneable. You can do a lot of things with a blob. In this case, I'm going to take the very first file that matches whose type is an image. And I'll just set the source of my output image to be whatever the image is. So, this is a drag and drop. And you can see it sets the source image to a URL that's been created from that blob. So that's how we're getting an image when we don't have direct access to the camera, which gives us a great progressive enhancement story, some good places to fall back to. But for accessing the camera directly, we'll talk about that next time. So join me then and thank you for watching. Thanks for watching. If you'd like to see more of our videos, click here and see you again. Cheers.