Responsive Web Design Niels Olof Bouvin 1 Overview - - PowerPoint PPT Presentation

responsive web design
SMART_READER_LITE
LIVE PREVIEW

Responsive Web Design Niels Olof Bouvin 1 Overview - - PowerPoint PPT Presentation

Responsive Web Design Niels Olof Bouvin 1 Overview Responsiveness? Responsiveness through scripting Events in the Web browser Web browser APIs The Number Guessing Game Talking to servers Responsiveness through styling Maps in the Browser


slide-1
SLIDE 1

Responsive Web Design

Niels Olof Bouvin

1

slide-2
SLIDE 2

Overview

Responsiveness? Responsiveness through scripting Events in the Web browser Web browser APIs The Number Guessing Game Talking to servers Responsiveness through styling Maps in the Browser

2

slide-3
SLIDE 3

Responsiveness?

A Website should adapt to its user

providing a quickly updating user interface adapt its presentation to the user’s device

We will look at this from two sides

adding JavaScript to Web pages in order to avoid complete reloads extending our CSS layout, so that we can support both wide and narrow screens

responsiveness | rɪˈspɒnsɪvnəs |

noun [mass noun]

the quality of reacting quickly and positively

3

slide-4
SLIDE 4

Overview

Responsiveness? Responsiveness through scripting Events in the Web browser Web browser APIs The Number Guessing Game Talking to servers Responsiveness through styling Maps in the Browser

4

slide-5
SLIDE 5

Back to the Web browser

One of the most transformational developments in Web design and development was the realisation that you could break free of the pure client/server model Rather than having to reload the entire Web page from the server, parse, and render it (again) to refmect some change, it became possible to have a script on the page retrieve changes from the server, and update the Web page in situ

much smaller download, no need to re-render ⇒ much faster, fmuid and responsive

5

slide-6
SLIDE 6

Overview

Responsiveness? Responsiveness through scripting Events in the Web browser Web browser APIs The Number Guessing Game Talking to servers Responsiveness through styling Maps in the Browser

6

slide-7
SLIDE 7

Events, events, events

The Web browser is governed by events

the page has loaded, or a resource has loaded the user has clicked something, or dragged something, or dropped something the user has resized the browser window

If we want something to happen when an event

  • ccurs, we have to subscribe to that event, or, in

proper Web browser parlance register an event handler

7

slide-8
SLIDE 8

An event listener: main.js

Whenever the button is clicked, the registered function is called (with the event as argument), and it modifjes the button’s background colour

app └── index.js public/ ├── index.html ├── js │ └── main.js └── style └── main.css const btn = document.querySelector('#button') function randomInt (limit = 256) { return Math.floor(limit * Math.random()) } function bgcChange (e) { const rndCol = `rgb(${randomInt()}, ${randomInt()}, ${randomInt()})` console.log(e) e.target.style.backgroundColor = rndCol } btn.addEventListener('click', bgcChange) … <body> <button id="button">Press this Button!</button> </body> </html>

8

slide-9
SLIDE 9

A selection of events

for all elements

click dblclick focus blur mouseover mouseout


for windows

keypress keydown keyup

9

slide-10
SLIDE 10

Adding content via JavaScript

Just as we can change the styling of existing elements

  • n a page, we can add new elements programmatically

you will recall the Document Object Model, which forms a tree of element rooted in <html> here, we added new children to the <body> element

for (let i = 1; i <= 32; i++) { const myDiv = document.createElement('div') document.body.appendChild(myDiv) } const divs = document.querySelectorAll('div') for (let myDiv of divs) { myDiv.addEventListener('mouseover', bgcChange) } app └── index.js public/ ├── index.html ├── js │ └── main.js └── style └── main.css

10

slide-11
SLIDE 11

Form validation

It can be a great convenience to be able to validate form entry before it actually makes it to the server (where we of course also have to validate it)

never, ever trust the user’s input

But, there is already a default action for pressing the Submit button on a form, so what to do? Events can be prevented from happening, until such time we are ready to permit them to execute

11

slide-12
SLIDE 12

The form: form.hbs

I have added a couple of empty, but named, <div> which will be used for error messages

<form method="POST" action="/greetings" id="greetings-form"> <div> <label for="recipient">Recipient:</label> <input type="text" id="recipient" name="recipient"> <div id="recipient-feedback" class="feedback"></div> </div> <div> <label for="message">Message:</label> <textarea id="message" name="message"></textarea> <div id="message-feedback" class="feedback"></div> </div> <div> <button type="submit">Submit</button> </div> </form>

app └── index.js db ├── db.js └── greetings.sqlite public ├── js │ └── main.js └── style └── main.css views ├── form.hbs ├── greeting.hbs └── layouts └── main.hbs

12

slide-13
SLIDE 13

The code: main.js

If a fjeld is empty, we put out an error message, and prevent the submit event from occurring

const form = document.querySelector('#greetings-form') const recipient = document.querySelector('#recipient') const message = document.querySelector('#message') const recipientFeedback = document.querySelector('#recipient-feedback') const messageFeedback = document.querySelector('#message-feedback') form.addEventListener('submit', (e) => { if (recipient.value === '') { e.preventDefault() recipientFeedback.textContent = 'Please add a recipient' } else { recipientFeedback.textContent = '' } if (message.value === '') { e.preventDefault() messageFeedback.textContent = 'Please add a message' } else { messageFeedback.textContent = '' } })

app └── index.js db ├── db.js └── greetings.sqlite public ├── js │ └── main.js └── style └── main.css views ├── form.hbs ├── greeting.hbs └── layouts └── main.hbs

13

slide-14
SLIDE 14

Form validation in action

14

slide-15
SLIDE 15

Event capturing and bubbling

For all elements on a Web page, we can add event listeners But what happens if we add the same kind of event listener (e.g., ‘click’) on elements that are contained within each other? Who gets the event?

the parent element (the ‘outer’ element), or the child element (‘the ‘inner’ element)?

15

slide-16
SLIDE 16

Bad bunny

Clicking the video is supposed to play it Clicking the surrounding area is supposed to hide it

16

slide-17
SLIDE 17

The body: index.html

The video element is contained within the <div> which has the class ‘hidden’ from the start

(incidentally, this shows you how to add video to your pages, and how to support multiple formats)

app └── index.js public ├── index.html ├── js │ └── main.js ├── style │ └── main.css └── video ├── rabbit320.mp4 └── rabbit320.webm

<body> <button>Display video</button> <div class="hidden"> <video> <source src="video/rabbit320.webm" type="video/webm"> <source src="video/rabbit320.mp4" type="video/mp4"> <p>Your browser doesn't support HTML5 video. Here is a <a href="video/rabbit320.mp4">link to the video</a> instead.</p> </video> </div> </body>

17

slide-18
SLIDE 18

The style: main.css

app └── index.js public ├── index.html ├── js │ └── main.js ├── style │ └── main.css └── video ├── rabbit320.mp4 └── rabbit320.webm

div { position: absolute; top: 50%; transform: translate(-50%,-50%); width: 480px; height: 380px; border-radius: 10px; background-color: #eee; background-image: linear-gradient(to bottom, rgba(0,0,0,0), rgba(0,0,0,0.1)); } .hidden { left: -50%; /* off screen */ } .showing { left: 50%; /* on screen */ } div video { display: block; width: 400px; margin: 40px auto; }

18

slide-19
SLIDE 19

The script: main.js

Seems straight forward enough:

click on the button: show the videoBox click on the videoBox: hide the videoBox click on the video: play the video so why does it not work?

app └── index.js public ├── index.html ├── js │ └── main.js ├── style │ └── main.css └── video ├── rabbit320.mp4 └── rabbit320.webm

const btn = document.querySelector('button') const videoBox = document.querySelector('div') const video = document.querySelector('video') btn.addEventListener('click', (e) => { videoBox.setAttribute('class', 'showing') }) videoBox.addEventListener('click', (e) => { videoBox.setAttribute('class', 'hidden') }) video.addEventListener('click', (e) => { video.play() })

19

slide-20
SLIDE 20

What happens when an event occurs?

Oddly enough, two things:

Event capturing: the browser starts from the outer-most ancestor (<html>), checks if it is registered for the event in the capturing phase, runs it if so, and then continues to the next element and so on, until it hits the inner-most element where the event occurred Event bubbling: the browser checks if the element which had the event occur is registered for that event in the bubbling phase, runs it if so, and then continues

  • utwards until it hits the outer-most element (<html>)

20

slide-21
SLIDE 21

So what is happening in the code?

In modern browsers, the default is event bubbling When the user clicks on the video, it starts playing, but the event continues upwards until the hits <html>

thus, the event listener on the <div> is also triggered, causing the <div> to be hidden

Happily, it is possible to stop events from propagating

e.stopPropagation()

21

slide-22
SLIDE 22

The fjxed script: main.js

Sometimes it can be convenient that events bubble up, and sometimes you will want to curtail that

app └── index.js public ├── index.html ├── js │ └── main.js ├── style │ └── main.css └── video ├── rabbit320.mp4 └── rabbit320.webm

const btn = document.querySelector('button') const videoBox = document.querySelector('div') const video = document.querySelector('video') btn.addEventListener('click', (e) => { videoBox.setAttribute('class', 'showing') }) videoBox.addEventListener('click', (e) => { videoBox.setAttribute('class', 'hidden') }) video.addEventListener('click', (e) => { e.stopPropagation() video.play() })

22

slide-23
SLIDE 23

Overview

Responsiveness? Responsiveness through scripting Events in the Web browser Web browser APIs The Number Guessing Game Talking to servers Responsiveness through styling Maps in the Browser

23

slide-24
SLIDE 24

So, that’s events. What’s else you got?

Web browsers can do a lot of things these days

manipulate the DOM, obviously draw on the page manipulate sound and video do 3D graphics access sensors found on the machine the Web browser is running on talk to servers

24

slide-25
SLIDE 25

Overview

Responsiveness? Responsiveness through scripting Events in the Web browser Web browser APIs The Number Guessing Game Talking to servers Responsiveness through styling Maps in the Browser

25

slide-26
SLIDE 26

JavaScript in the Web browser

The central object in the Web browser, document,

  • ffer access to the Web page, enabling us to setup

event listeners, read state of elements, modify, and add content to the page The Number Guessing Game is a small example of what is possible Follow the MDN page on the game, but note the changes I have made to the game in the following pages

26

slide-27
SLIDE 27

A game on a Web page It generates a random number as a target It accepts the user's guess through a text fjeld It checks the user's guess against the target It updates the Web page by adding elements This is repeated until the game is won or lost The game can then be restarted and the page reset

The Number Guessing Game

27

slide-28
SLIDE 28

The Web page

<!DOCTYPE html> <html lang=“en”> <head> <meta charset="utf-8"> <title>Number guessing game</title> <link rel="stylesheet" href="game.css"> </head> <body> <h1>Number guessing game</h1> <p>We have selected a random number between 1 and 100. See if you can guess it in 10 turns or fewer. We'll tell you if your guess was too high or too low.</p> <div class="form"> <label for="guessField">Enter a guess: </label> <input type="text" id="guessField"> <input type="submit" value="Submit guess" id="guessSubmit"> </div> <div id="resultParas"> <p id="guesses"></p> <p id="lastResult"></p> <p id="lowOrHi"></p> </div> <script src="game.js"></script> </body> </html>

28

slide-29
SLIDE 29

The stylesheet

html { font-family: sans-serif; } body { width: 50%; max-width: 800px; min-width: 480px; margin: 0 auto; } #lastResult { color: white; padding: 3px; }

29

slide-30
SLIDE 30

The code

let randomNumber = Math.ceil(Math.random() * 100) const guesses = document.querySelector('#guesses') const lastResult = document.querySelector('#lastResult') const lowOrHi = document.querySelector('#lowOrHi') const guessSubmit = document.querySelector('#guessSubmit') const guessField = document.querySelector('#guessField') let guessCount = 1 let resetButton function checkGuess () { let userGuess = Number(guessField.value) if (guessCount === 1) { guesses.textContent = 'Previous guesses: ' } guesses.textContent += userGuess + ' ' if (userGuess === randomNumber) { lastResult.textContent = 'Congratulations! You got it right!' lastResult.style.backgroundColor = 'green' lowOrHi.textContent = '' setGameOver() } else if (guessCount === 10) { lastResult.textContent = '!!!GAME OVER!!!' setGameOver() } else { lastResult.textContent = 'Wrong!' lastResult.style.backgroundColor = 'red' if (userGuess < randomNumber) { lowOrHi.textContent = 'Last guess was too low!' } else if (userGuess > randomNumber) { lowOrHi.textContent = 'Last guess was too high!' } } guessCount++ guessField.value = '' guessField.focus() } guessSubmit.addEventListener('click', checkGuess) function setGameOver () { guessField.disabled = true guessSubmit.disabled = true resetButton = document.createElement('button') resetButton.textContent = 'Start new game' document.body.appendChild(resetButton) resetButton.addEventListener('click', resetGame) } function resetGame () { guessCount = 1 const resetParas = document.querySelectorAll('#resultParas p') for (let rP of resetParas) { rP.textContent = '' } resetButton.parentNode.removeChild(resetButton) guessField.disabled = false guessSubmit.disabled = false guessField.value = '' guessField.focus() lastResult.style.backgroundColor = 'white' randomNumber = Math.ceil(Math.random() * 100) }

30

slide-31
SLIDE 31

The game code

The game demonstrates several things

accessing the Web page through the Document Object Model fjnding, adding, modifying, and removing elements from the DOM adding event listeners to elements in the DOM, and reacting when triggered

This forms the basis for much of what goes on in interactive Web pages

minus getting data from the server, but we will get to that next

31

slide-32
SLIDE 32

Overview

Responsiveness? Responsiveness through scripting Events in the Web browser Web browser APIs The Number Guessing Game Talking to servers Responsiveness through styling Maps in the Browser

32

slide-33
SLIDE 33

Talking to servers

The XMLHttpRequest class makes it possible to access Web servers from JavaScript in the Web browser This is crucial functionality, as it allows to retrieve information from Web servers as well as initiating HTTP requests that we cannot do through the location bar

such as invoking the HTTP DELETE method

33

slide-34
SLIDE 34

Greetings: index.js & greeting.hbs

I have added a new route to the Express server, and I have added a ‘Delete’ button to the greetings’ view

… app.delete('/greetings/:id', (request, response, next) => { const id = request.params.id Greetings.delete(id, (err) => { if (err) return next(err) }) }) …

app └── index.js db ├── db.js └── greetings.sqlite public ├── js │ └── main.js └── style └── main.css views ├── form.hbs ├── greeting.hbs └── layouts └── main.hbs

{{#each greetings}} <h2>Hello {{recipient}}</h2> <p>{{message}} <button data-id="{{id}}" class="delete">Delete</button></p> {{/each}} <div><a href="/form">Add a greeting</a></div>

34

slide-35
SLIDE 35

data- attributes

You may add data-whatever attributes to any element It is a very handy way to insert data for later use into a Web page

35

slide-36
SLIDE 36

Sending a DELETE request: main.js

I create a XMLHttpRequest object with the DELETE method and a URL of the form /greetings/:id

after it has been sent, I force a reload of the page to show the remaining greetings

app └── index.js db ├── db.js └── greetings.sqlite public ├── js │ └── main.js └── style └── main.css views ├── form.hbs ├── greeting.hbs └── layouts └── main.hbs

… const greetings = document.querySelectorAll('.delete') function deleteThisGreeting (e) { const requestURL = `${document.URL}/${e.target.attributes['data-id'].value}` const request = new XMLHttpRequest() request.open('DELETE', requestURL) request.send() window.location.replace(document.URL) } if (greetings) { for (let myGreet of greetings) { myGreet.addEventListener('click', deleteThisGreeting) } }

36

slide-37
SLIDE 37

The greetings site in action

37

slide-38
SLIDE 38

DOM manipulation, continued

We have seen that it is easy, using createElement() and appendChild(), to create and add elements to the DOM Using removeChild(), we can remove an element, provided that we know its parent:

parentElement.removeChild(unwantedChild)

So, if we can arrange it so that we do know the parent, things are pretty straightforward

38

slide-39
SLIDE 39

New greetings: greeting.hbs

I have wrapped all greetings in an IDed <div>, as well as wrapping every greeting in a <div> with an ID of the form ‘greeting-id’ , so it will be easy to fjnd

app └── index.js db ├── db.js └── greetings.sqlite public ├── js │ └── main.js └── style └── main.css views ├── form.hbs ├── greeting.hbs └── layouts └── main.hbs

<div id="greetings"> {{#each greetings}} <div id="greeting-{{id}}"> <h2>Hello {{recipient}}</h2> <p>{{message}} <button class="delete" data-id="{{id}}">Delete</button></p> </div> {{/each}} </div> <div><a href="/form">Add a greeting</a></div>

39

slide-40
SLIDE 40

DELETE on server and page: main.js

└── index.js db ├── db.js └── greetings.sqlite public ├── js │ └── main.js └── style └── main.css views ├── form.hbs ├── greeting.hbs └── layouts └── main.hbs

const deleteButtons = document.querySelectorAll('.delete') const greetings = document.querySelector('#greetings') function deleteThisGreetingOnServer (e) { const id = e.target.attributes['data-id'].value const requestURL = `${document.URL}/${id}` const request = new XMLHttpRequest() request.open('DELETE', requestURL) request.send() deleteThisGreetingOnPage(id) } function deleteThisGreetingOnPage (id) { const selector = `#greeting-${id}` const theGreeting = document.querySelector(selector) greetings.removeChild(theGreeting) } if (deleteButtons) { for (let myButton of deleteButtons) { myButton.addEventListener('click', deleteThisGreetingOnServer) } }

40

slide-41
SLIDE 41

DOM deletion in action

41

slide-42
SLIDE 42

The DOM is there for your use

We can dynamically add and delete from the DOM as we see fjt This enables fmexible and responsive Web pages

42

slide-43
SLIDE 43

Overview

Responsiveness? Responsiveness through scripting Events in the Web browser Web browser APIs The Number Guessing Game Talking to servers Responsiveness through styling Maps in the Browser

43

slide-44
SLIDE 44

Layout in CSS

Layout has always been difficult on the Web

for many years, there was not any good support for it

Then CSS came along, and some things improved a lot But, most layout was based on a fmawed assumption: “We know the proportions & size of the users’ displays” Which was true in the PC era, but as smartphones progressed it became increasingly problematic

44

slide-45
SLIDE 45

Flexbox: one dimensional layout

Many layouts involve elements that should adapt to the available space, possibly sharing it in an equitable way with other elements The fmexbox is well suited for

vertically aligning an element within its parent making all children of a container take an equal part of the available space making a multi-column layout where the columns are of equal height

45

slide-46
SLIDE 46

An MDN example: index.html

<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Flexbox Basics</title> <link rel="stylesheet" type="text/css" media="screen" href="style/main.css" /> </head> <body> <header> <h1>Sample flexbox example</h1> </header> <section> <article> <h2>First article</h2> <p>…</p> </article> <article> <h2>Second article</h2> <p>…</p> </article> <article> <h2>Third article</h2> <p>…</p> <p>…</p> </article> </section> </body> </html>

app └── index.js public ├── index.html └── style └── main.css

46

slide-47
SLIDE 47

An MDN example: main.css

html { font-family: sans-serif; } body { margin: 0; } header { background: purple; height: 100px; } h1 { text-align: center; color: white; line-height: 100px; margin: 0; } article { padding: 10px; margin: 10px; background: aqua; }

app └── index.js public ├── index.html └── style └── main.css

47

slide-48
SLIDE 48

Adding a bit of fmex

html { font-family: sans-serif; } body { margin: 0; } header { background: purple; height: 100px; } h1 { text-align: center; color: white; line-height: 100px; margin: 0; } article { padding: 10px; margin: 10px; background: aqua; } section { display: flex; }

app └── index.js public ├── index.html └── style └── main.css

48

slide-49
SLIDE 49

Changing directions: column

html { font-family: sans-serif; } body { margin: 0; } header { background: purple; height: 100px; } h1 { text-align: center; color: white; line-height: 100px; margin: 0; } article { padding: 10px; margin: 10px; background: aqua; } section { display: flex; flex-direction: column; }

app └── index.js public ├── index.html └── style └── main.css

49

slide-50
SLIDE 50

Changing directions: row

html { font-family: sans-serif; } body { margin: 0; } header { background: purple; height: 100px; } h1 { text-align: center; color: white; line-height: 100px; margin: 0; } article { padding: 10px; margin: 10px; background: aqua; } section { display: flex; flex-direction: row; }

app └── index.js public ├── index.html └── style └── main.css

50

slide-51
SLIDE 51

Changing proportions

html { font-family: sans-serif; } body { margin: 0; } header { background: purple; height: 100px; } h1 { text-align: center; color: white; line-height: 100px; margin: 0; } article { padding: 10px; margin: 10px; background: aqua; } section { display: flex; } article { flex: 1; } article:nth-of-type(3) { flex: 2; }

app └── index.js public ├── index.html └── style └── main.css

51

slide-52
SLIDE 52

CSS Grid

A (part of a) page is divided into a grid by numbered lines, and content can then be distributed between the cells within those lines

❶ ❶ ❷ ❸ ❹ ❷ ❸ ❹

52

slide-53
SLIDE 53

An example: index.html

<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Grid Basics</title> <link rel="stylesheet" type="text/css" media="screen" href="style/ main.css" /> </head> <body> <div class="wrapper"> <div class="box">One</div> <div class="box">Two</div> <div class="box">Three</div> <div class="box">Four</div> <div class="box">Five</div> <div class="box">Six</div> </div> </body> </html> app └── index.js public ├── index.html └── style └── main.css

53

slide-54
SLIDE 54

An example: main.css

This is just as you would expect: the divs naturally expand to fjll the width of the screen

body { background-color: whitesmoke; } div { border: solid 1px saddlebrown; padding: 1em; border-radius: 5px; } .box { background-color: indianred; } .wrapper { } app └── index.js public ├── index.html └── style └── main.css

54

slide-55
SLIDE 55

An example: main.css

We are now using grid layout, but it is implicitly or automatically created, leaving no discernible visual difference

body { background-color: whitesmoke; } div { border: solid 1px saddlebrown; padding: 1em; border-radius: 5px; } .box { background-color: indianred; } .wrapper { display: grid; } app └── index.js public ├── index.html └── style └── main.css

55

slide-56
SLIDE 56

An example: main.css

Let’s add some columns…

the layout implicitly/automatically repeats for the remaining divs

.wrapper { display: grid; grid-template-columns: 100px 200px 300px; } app └── index.js public ├── index.html └── style └── main.css

56

slide-57
SLIDE 57

An example: main.css

Let’s add some different columns…

the layout automatically repeats for the remaining divs

.wrapper { display: grid; grid-template-columns: 200px 300px; } app └── index.js public ├── index.html └── style └── main.css

57

slide-58
SLIDE 58

An example: main.css

Let’s add both columns and rows

‘fr’ defjnes a fraction of the available space within the enclosing container

.wrapper { display: grid; grid-template-columns: 100px 300px 100px; grid-template-rows: 1fr 2fr } app └── index.js public ├── index.html └── style └── main.css

58

slide-59
SLIDE 59

Header, sidebar, main

<div class="wrapper"> <div class="header">Header</div> <div class="sidebar">Sidebar</div> <div class="main">Main</div> </div> app └── index.js public ├── index.html └── style └── main.css

.wrapper { border: solid 2px saddlebrown; display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 1fr); } .header { grid-column-start: 1; grid-column-end: 3; grid-row-start: 1; grid-row-end: 2; background-color: #85BDFD; } .main { grid-column-start: 1; grid-column-end: 3; grid-row-start: 2; grid-row-end: 4; background-color: #66B312; } .sidebar { grid-column-start: 3; grid-column-end: 4; grid-row-start: 2; grid-row-end: 4; background-color: D25EA8; }

❶❶ ❷ ❸ ❹ ❷ ❸ ❹

59

slide-60
SLIDE 60

Header, sidebar, main

<div class="wrapper"> <div class="header">Header</div> <div class="sidebar">Sidebar</div> <div class="main">Main</div> </div> app └── index.js public ├── index.html └── style └── main.css

.wrapper { border: solid 2px saddlebrown; display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 1fr) } .header { grid-column: 1 / 3; grid-row: 1 / 2; background-color: #85BDFD; } .main { grid-column: 1 / 3; grid-row: 2 / 4; background-color: #66B312; } .sidebar { grid-column: 3 / 4; grid-row: 2 / 4; background-color: #D25EA8; }

❶❶ ❷ ❸ ❹ ❷ ❸ ❹

60

slide-61
SLIDE 61

Header, sidebar, main

<div class="wrapper"> <div class="header">Header</div> <div class="sidebar">Sidebar</div> <div class="main">Main</div> </div> app └── index.js public ├── index.html └── style └── main.css

.wrapper { border: solid 2px saddlebrown; display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 1fr) } .header { grid-column: 1 / 3; grid-row: 1 / 2; background-color: #85BDFD; } .main { grid-column: 1 / 3; grid-row: 2 / 4; background-color: #66B312; } .sidebar { grid-column: 3 / 4; grid-row: 2 / 4; background-color: #D25EA8; }

61

slide-62
SLIDE 62

Becoming truly responsive

While both fmexbox and grid help with general layout and can create some really versatile designs, we are not quite responsive yet We need to take the available display (or ‘viewport’) into account This can be done directly in CSS using the @media() selector

62

slide-63
SLIDE 63

A responsive Grid example

app └── index.js public ├── index.html └── style └── main.css

<!DOCTYPE html> <html> <head> <title>Hello!</title> <meta charset="utf-8"> <link rel="stylesheet" href="style/main.css"> </head> <body class="container"> <header>Header</header> <nav>Navigation</nav> <main> <h1>Main</h1> <p>…</p> </main> <aside>Related links</aside> <footer>Footer</footer> </body> </html>

❶❶ ❷ ❸ ❹ ❷ ❸ ❹

63

slide-64
SLIDE 64

A responsive Grid example

app └── index.js public ├── index.html └── style └── main.css body { font-family: sans-serif; margin: 0 } .container { display: flex; flex-direction: column; min-height: 100vh; } @media (min-width: 768px) { .container { display: grid; grid-template-columns: 200px 1fr 200px; grid-template-rows: auto 1fr auto; } } header { grid-column: 1 / 4; padding: 30px; text-align: center; background-color: #369; color: white; }
 main { flex: 1; padding: 20px; } nav { background-color: #f90; padding: 20px; } aside { padding: 20px; background-color: #936; } footer { grid-column: 1 / 4; padding: 30px; text-align: center; background-color: #690; color: white; }

64

slide-65
SLIDE 65

A responsive Grid example

65

slide-66
SLIDE 66

Responsive Design Mode

66

slide-67
SLIDE 67

Responsive design

A major breakthrough in the past few years is that it is now possible to specify the layout of a Web page purely in CSS without the use of JavaScript With JavaScript, we add, remove, or modify content With CSS, we style it and adapt to the user’s display

67

slide-68
SLIDE 68

Overview

Responsiveness? Responsiveness through scripting Events in the Web browser Web browser APIs The Number Guessing Game Talking to servers Responsiveness through styling Maps in the Browser

68

slide-69
SLIDE 69

Maps in the Web browser

Considering the complexities and enormous amount work behind, it is pretty straightforward to display a map in your Web browser I will be using Open Street Maps in my examples, as they are open source

69

slide-70
SLIDE 70

Maps: index.html & main.css

Note how we include external JS and CSS resources here: this is quite common when using 3rd party APIs

app └── index.js public/ ├── index.html ├── js │ └── main.js └── style └── main.css <!doctype html> <html lang="en"> <head> <link rel="stylesheet" href="https://cdn.rawgit.com/openlayers/

  • penlayers.github.io/master/en/v5.3.0/css/ol.css" type="text/css">

<link rel="stylesheet" href="style/main.css" type="text/css"> <script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/ master/en/v5.3.0/build/ol.js"></script> <script defer src="js/main.js" type="text/javascript"></script> <title>OpenLayers example</title> </head> <body> <h2>My Map</h2> <div id="map" class="map"></div> </body> </html> .map { height: 400px; width: 100%; }

70

slide-71
SLIDE 71

The code behind maps: main.js

The fjrst line defjnes my home’s position, and then instantiates a new OpenLayer’s Map object set to retrieve map tiles from OpenStreetMap, and a view centred on my humble abode

app └── index.js public/ ├── index.html ├── js │ └── main.js └── style └── main.css const position = [10.155, 56.154] const map = new ol.Map({ target: 'map', layers: [ new ol.layer.Tile({ source: new ol.source.OSM() }) ], view: new ol.View({ center: ol.proj.fromLonLat(position), zoom: 12 }) })

71

slide-72
SLIDE 72

What about our actual position?

Web browsers support a geolocation API, enabling us to access the user’s actual position (measured using GPS or some other means)

navigator.geolocation.getCurrentPosition()

However, this is sensitive information, and Web browsers will not permit the use of this API on unsecured Web sites So, if we are using HTTP, we are out of luck

72

slide-73
SLIDE 73

The navigator object

The document is the root object that represents the document shown on the page The navigator object represents the Web browser and its capabilities You will probably mainly access it to get the user’s position

73

slide-74
SLIDE 74

Switching to HTTPS

Developing Web sites using HTTPS is quite a bit more labour intensive, as it is necessary to generate server certifjcates, that authenticates the Web server to the user, and enables encryption of the transmitted data We may later go into how you can use Let’s Encrypt’s (free) services to build secure Web sites, but for the purposes of this lecture, we will be using a Node module called ‘pem’

however, it does require a bit of clicking in the Web browser, as they are, justifjably, paranoid about self-signed certifjcates it is ok for testing and development, but not for production

74

slide-75
SLIDE 75

A secure server: index.js

app └── index.js public/ ├── index.html ├── js │ └── main.js └── style └── main.css 'use strict' const https = require('https') const express = require('express') const pem = require('pem') const path = require('path') const app = express() const port = 4430 pem.createCertificate({ days: 1, selfSigned: true }, function (err, keys) { if (err) throw err app.use(express.static(path.join(__dirname, ‘../public'))) https.createServer({ key: keys.serviceKey, cert: keys.certificate }, app).listen(4430, () => { console.log(`Listening on https://localhost:${port}/`) }) })

75

slide-76
SLIDE 76

Location detection: main.js

app └── index.js public/ ├── index.html ├── js │ └── main.js └── style └── main.css

if ('geolocation' in navigator) { navigator.geolocation.getCurrentPosition(function (position) { const myPosition = [position.coords.longitude, position.coords.latitude] const map = new ol.Map({ target: 'map', layers: [ new ol.layer.Tile({ source: new ol.source.OSM() }) ], view: new ol.View({ center: ol.proj.fromLonLat(myPosition), zoom: 12 }) }) }) } else { const para = document.createElement('p') para.textContent = 'Geolocation is not available' document.body.appendChild(para) }

76

slide-77
SLIDE 77

Self-signed and location in action

77