class: middle ### Manipulating text in javascript~ --- class: middle Fist, a quick detour on `arrow functions`. So far we've learned how to write functions like this: ```js function wakeUp() { console.log('time to wake up 😪'); } ``` Another way that we can write functions in a bit more concise way are called `arrow functions`: ```js let wakeUp = () => { console.log('time to wake up 😪'); } ``` You'll most often see them used with event listeners or whenever a callback function is needed: ```js element.addEventListener('click', (event) => { console.log('You clicked the element!!'); }); ``` --- class: middle #### strings A `string` is just a piece of text wrapped in either `''` single or `""` double quotes. Strings are made up of characters (like the `a` in "apple"). These sequences of characters *strung* together comprise a `string`
: )
```js let myPoem = 'A long string / stung my head / to recall it'; // Strings are used often in javascript document.querySelector('#my-blog'); ^ string here ``` --- class: middle Since `strings` are just a bunch of characters strung together, you can use them in a similar way to arrays: ```js let myString = 'hello'; console.log(myString[0]); ``` --- class: middle You can search for the index (position) of a character or word within a string with `indexOf()`: ```js let greeting = 'Hello how are you?'; // ^ how starts at the 6th index position let howIndex = greeting.indexOf('how'); console.log(howIndex); ``` If found, this will return the index of the start of the sequence of characters. --- class: middle You can also check whether a string occurs within another string with `includes`: ```css .sad { color: blue; } .happy { color: green; } ``` ```html
``` ```js let input = document.querySelector('#feeling'); // let's try using an arrow function input.addEventListener('keyup', (event) => { if (event.target.value.includes('sad')) { event.target.classList.remove('happy'); event.target.classList.add('sad'); } else if (event.target.value.includes('happy')) { event.target.classList.add('happy'); event.target.classList.remove('sad'); } else { event.target.classList.remove('happy', 'sad'); } }); ```
--- class: middle The `slice()` method enables us to return part of a string by specifying the start and end index we would like to *slice* by. Slice creates a new string and leaves the source alone. Slice takes at least one parameter, where to start the slice, and optionally where to end it. ```js 'hello'.slice(start, end); // or myStringVariable.slice(0, 5); ``` If you don't provide an end index, it will assume the rest of the string. --- class: middle Let's make our greeting informal: ```js let greeting = 'Hello sir, how are you?'; // find the index of how let howIndex = greeting.indexOf('how'); // slice the string to be 'how are you?' let informal = greeting.slice(howIndex); // capitalize the first characther ('h' in how) // and then return the rest of the string after that character informal = informal[0].toUpperCase() + informal.slice(1); console.log(informal); ``` --- class: middle You can split up strings by a character with the `split()` method: ```js let text = 'Hello class. Hope this is all very clear. If not, then pls ask a question.'; ``` The `split()` method will split up the string based on the character you provide and return them as an array: ```js // spilt our text by the periods let sentences = text.split('.'); console.log(sentences); ``` which results in: ```js ["Hello class", " Hope this is all very clear", " If not, then pls ask a question", ""] // ^ spaces after the period will be included ``` --- class: middle We can recombine an array of strings with the `join()` method: ```js let emphasis = sentences.join('!!!'); console.log(emphasis); ``` --- class: middle We could break out a sentence into individual paragraph elements using javascript's `document.createElement()` method and append them to a container element with `append()`: ```html
``` ```js let poemString = 'Rain in winter— / unhappy pine tree / longs for snow'; let poemLines = poemString.split('/'); let poemContainer = document.querySelector('#poem'); poemLines.forEach((line) => { // create a new paragraph element let line = document.createElement('p'); // set the contents of the paragraph to our line at index i line.innerText = line; poemContainer.append(line); }); ```
--- class: middle We often end up needing to combine strings and variables to generate new strings. One way to achieve this is through `concatination` or adding things together: ```js let weather = 'windy' // or get this from a weather service let info = 'Today the weather is ' + weather + '. Have a good day!'; console.log(info) ``` --- class: middle A simpler way to combine strings and variables is with
`template strings`. Template strings are a special kind of string definition using \` `backticks` instead of quotes " or ': ```js let weather = 'rainy'; let gear = 'umbrella'; let info = `Today is ${weather}, hope you brought your ${gear}`; console.log(info); ``` With template strings, we can substitute in any variables within the string with the special `${ myVariable }` decleration. Anything within the `${` and `}` will be inserted in that position into the string. --- class: middle We could move our weather template string to a function and pass in the condition variables as arguments to have a flexible weather greeting: ```js function weatherNotice(weather, gear) { return `Today is ${weather}, hope you brought your ${gear}!`; } console.log(weatherNotice('rainy', 'umbrella')); // later that day console.log(weatherNotice('sunny', 'sunglasses')); console.log(weatherNotice('snowy', 'snow pants')); ``` --- class: middle [There are a ton of things you can do with strings](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) --- class: middle center # ~ Web APIs & Advanced JS ~ --- class: middle ## Advanced JS: Web APIs An `API` or Application Programming Interface is a way to refer to and utilize an external resource that you interact with using code. --- class: middle There are a [bunch special APIs](https://developer.mozilla.org/en-US/docs/Web/API) that browsers support, some of the more interesting ones: - Geolocation - Notification - Clipboard - Fetch - localStorage & sessionStorage --- class: middle ## Geolocation API The [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API) requests the latitude and longitude coordinates of the user's location. This will vary in quality based on the device. Most of these Web APIs require special permission from the user in browser to enable them. The geolocation API is accessed with: `navigator.geolocation` --- class: middle Let's create a simple example: ```html
Show Location
``` ```js let bin = document.querySelector('#location'); function showLocation(pos) { bin.innerHTML = `Your location is: ${pos.coords.latitude}, ${pos.coords.longitude}` } function showError(error) { bin.innerHTML = error.message; } let button = document.querySelector('#request-location'); button.addEventListener('click', function () { navigator.geolocation.getCurrentPosition(showLocation, showError); }); ```
Show Location
--- class: middle ## Notification API The [Notifications API](https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API) is a simple tool to add popup messages at the operating system level, outside the web page context. --- class:middle ## User dependent actions A new concept we need to introduce in order to work with some of these APIs is called a `Promise`. `Promises` are a bit of an advanced concept, but you'll start to see them more often the further along you go with javascript. They solve a simple problem that can help explain what they are. With the notification API we need to ask the user for permission to show them, which could take an unknown amount of time to answer.. what if they've left the room or are multi-tasking. `Promises` offer a way to handle the execution of some code without `blocking` or preventing the rest of your javascript from running. --- class: middle Since `Promises` don't block the execution of code while we wait for them to resolve, we need a way to do something once they do finally finish. For now, let's just focus on this second part. The Notifications API returns a `promise` when requesting the user for permission. We use the special `.then()` property that allows us to attach a function that will be called as soon as the `promise` is resolved. --- class: middle ```html
Request Permission
``` ```js function permissionResponse(response) { console.log('The user answered', response); } function getPermission() { // this returns a promise Notification.requestPermission().then(permissionResponse); console.log('requested permission... waiting!'); } documnet.querySelector('#notify').addEventListener('click', getPermission); ``` Above we have a function `getPermission` that uses the Web Notification API to ask the user permission to display notifications. Since it uses `Promises` our code won't stop and hang at this point, waiting for the user's response. Instead, it'll keep going while we wait, and print the following `console.log`.
Request Permission
--- class: middle Now let's put it together for a full example: ```html
Send Notification
``` ```js function sendNotification() { new Notification('New Notification', {body: 'This is an example notification', icon: '😁'}); } function getPermission() { console.log('requesting permission..'); Notification.requestPermission().then(sendNotification); } let btn = document.querySelector('#request-permission'); btn.addEventListener('click', getPermission); ```
Send Notification
--- class: middle ## Clipboard API Since javascript runs in our browser, it has access to a lot of things our browser is provided access to from our computers, like the clipboard. Let's say we wanted to create a simple button that would convert any text in your clipboard to uppercase. With the [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard) we can use the two methods to do this, called read (to access) and write (to update). --- class: middle Good morning everyone! ```html
Click this to scream
``` ```js let screamButton = document.querySelector('#scream'); screamButton.addEventListener('click', (event) => { navigator.clipboard.readText().then((text) => { navigator.clipboard.writeText(text.toUpperCase()); }); }); ```
Click this to scream
--- class: middle ## Fetch `fetch` is a useful way to load external websites from within javascript. --- class: middle A common use for an API is a website you can load to receive data. For instance a weather API would be a website you could load with the city you're in to return forecast data. Data returned from APIs are often formatted in `JSON` (pronounced j-son or jason) or javascript (js) object notation. JSON is simply a way to represent a bunch of info in a structured way. Let's use [this sunrise/sunset api](https://sunrise-sunset.org/api) based on a provided location as an example. --- class: middle Since we already know how to get the latitude and longitude coordinates for a user, lets look at how we can use `fetch` to call this sunset api, supplying that info. Like the Notifications API, fetch returns a promise, since loading something from the web can take an unknown amount of time, just like a user response. --- class: middle two-column ## A quick aside about url parameters We want to load the sunset api with our specific location to learn what time sunset is. One common way to attach info to our request is to use `query strings`. `https://api.sunrise-sunset.org/json?lat=36.72&lng=-4.42` You've likely seen them before at the end of urls. The `query string` is everything after the `?` formatted `
=
` with `&` chaining them together. In our api example above `?lat=36.72&lng=-4.42` splits up to be: - `lat` = `36.72` - `lng` = `-4.42` Code running on the server for a website can receive these parameters and respond accordingly. --- class: middle So, now we know how to supply the coordinates to the api, let's try calling it with `fetch`: ```js fetch('https://api.sunrise-sunset.org/json?lat=40.74573&lng=-73.9244437') .then(function (res) { console.log(res); }); ``` The above code calls the `fetch()` which returns a `promise` that will *resolve* once the website loads and returns a response. We are chaining on the `.then()` to receive this response and log it to the console. It's condensed to be succinct, but this could also be written: ```js function handleRequest(req) { console.log(req); } let getApi = fetch('https://api.sunrise-sunset.org/json?lat=40.74573&lng=-73.9244437'); getApi.then(handleRequest); ``` --- class: middle The response from `fetch`, if it was successful, is a *http response* and not yet the usable `JSON` sunset data: ```js Response { type: "cors", url: "https://api.sunrise-sunset.org/json?lat=40.74573&lng=-73.9244437", redirected: false, status: 200, ok: true, statusText: "OK", headers: Headers, body: ReadableStream, bodyUsed: false } ``` To unpack this and see the actual data, we need to get the `JSON` from the response with `.json()`: ```js function handleRequest(req) { // unpack json req.json().then(function (data) { console.log(data); }); } ``` The `json()` method also conveniently returns a `promise` so we can once again use `.then()` to attach callback that will receive the `JSON` data once it has been extracted. --- class: middle So putting it all together: ```js function logSunsetData(data) { console.log(data); } function handleRequest(res) { res.json().then(logSunsetData); } let apiFetch = fetch('https://api.sunrise-sunset.org/json?lat=40.74573&lng=-73.9244437'); apiFetch.then(handleRequest); ``` Or you will often seen this written more concisely: ```js fetch('https://api.sunrise-sunset.org/json?lat=40.74573&lng=-73.9244437') .then(function (res) { return res.json(); }) .then(function (data) { console.log(data); }); ``` --- class: middle Now that we know how to both request the user's coordinates and get the sunset data for them, we can create a widget that will group it all together. One last detail is that the sunset api returns the date in GMT. We can convert that to our computer's local time using the Javascript `Date` method: ```js let sunset = '11:40:29 PM'; // nyc aka gmt-4 so 7:40 pm let today = new Date(); let sunsetToday = new Date(`${today.toDateString()} ${sunset} GMT`); console.log(sunsetToday.toLocaleTimeString()); ``` --- class: middle ```html
When is sunset?
``` ```js let sunsetDisplay = document.querySelector('#sunset-display'); function displaySunset(data) { let sunset = data.results.sunset; let today = new Date(); let sunsetToday = new Date(`${today.toDateString()} ${sunset} GMT`); sunsetDisplay.innerHTML = `Sunset will occur at ${sunsetToday.toLocaleTimeString()}`; } function receiveCoordinates(position) { const latitude = position.coords.latitude; const longitude = position.coords.longitude; fetch(`https://api.sunrise-sunset.org/json?lat=${latitude}&lng=${longitude}`) .then(function (response) { response.json().then(displaySunset); }); } function showError(err) { sunsetDisplay.innerHTML = err.message; } document.querySelector('#show-sunset').addEventListener('click', function () { navigator.geolocation.getCurrentPosition(receiveCoordinates, showError); }); ``` --- class: middle center
When is sunset?
--- class: middle ## localStorage and sessionStorage Quickly, let's look at two last browser APIs we can utilize. --- class: middle two-columns `localStorage` provides a way to store text and other data with the user's browser. This information you save will *persist* or be available after they refresh, close the browser or restart their computer. In this way, `localStorage` acts like a *database* or repository of information that you can set and retrieve later all relative to the user's browser. A few caveats: - localStorate is tied to the browser, so if you set something in one browser it won't be available in another one - incognito or other user configured restrictions might block accessf to localStorage - user's can clear this data when they clear their browser history --- class: middle `localStorage` is a great way to store any kind of custom settings or options, for instance if you allow your user to customize the UI or set any preferences, you can store these items in localStorage so that on future visits you can load them and preserve the settings. --- class: middle It is pretty simple to use, you can `set`, `get` and `remove` items in storage: ```js // save the sunset from previous example localStorage.setItem('sunsetTime', sunset); // later or after refresh we could retrieve let sunsetTime = localStorage.getItem('sunsetTime'); console.log(sunsetTime); // note: items will always be stored as a string localStorage.setItem('favNumber', 12); // will return "12" console.log(localStorage.getItem('favNumber')); // you can also remove items from storage localStorage.removeItem('sunsetTime'); ``` --- class: middle You can also store objects, arrays and other complex items like JSON. The only thing you need to do is convert them to a string before storing with `JSON.stringify()` ```js localStorage.setItem('sunset', JSON.stringify(sunsetData)); ``` We can see whether we have a stored item by first using `getItem` and then checking whether the returned value is `null` or not: ```js // we stored our favorite number, but not color ; ) let favColor = localStorage.getItem('favColor'); console.log(favColor); // returns null // or could use with a conditional if (favColor) { // customize the background color..? } ``` --- class: middle Full example: ```js let myFavoriteThings = { color: 'green', flavor: 'umami', sound: 'waterfall', scent: 'geranium', place: 'natural spaces' }; localStorage.setItem('favoriteThings', JSON.stringify(myFavoriteThings)); // some later date let storedFavoriteThings = localStorage.getItem('favoriteThings'); // if the item doesn't exist, it will be `null` so we should check for that if (storedFavoriteThings) { // to convert back to an object we can let myFavoriteThings = JSON.parse(storedFavoriteThings); console.log('We have your favorite things', myFavoriteThings); } ``` To convert back to an array, object, etc we can use `JSON.parse()` --- class: middle ## sessionStorage `sessionStorage` is just like localStorage except that it only lasts for the *session* aka for however long the tab or browser is open. It's a way to store things that need less permanence.