-
What's new for web developers
Explore the latest features and improvements for Safari and WebKit. We'll walk you through updated web APIs, CSS and media features, JavaScript syntax, and more to help you build great experiences for people when they use your website, home screen web apps, or embedded WebKit views.
Recursos
Videos relacionados
WWDC20
-
Buscar este video…
-
-
4:22 - Web Animations API code example
// Web Animations API Code Example let needle = document.getElementById("needle"); let logo = document.getElementById("logo"); logo.addEventListener("click", () => { needle.animate({ transform: [ "rotateX(35deg) rotateZ(13deg)", "rotateX(35deg) rotateZ(733deg)", ], easing: ["ease-out"], }, 800); }); -
6:43 - Resize observer example
// Resize Observer Example let formatPanelObserver = new ResizeObserver((entries) => { entries.forEach((entry) => { let container = entry.target; container.classList.toggle("small", entry.contentRect.width < 175); } }); formatPanelObserver.observe(document.getElementById("format-panel")); -
8:15 - Async Clipboard API plain text programmatic copy
// Programmatic copy copyButtonElement.addEventListener("click", (event) => { navigator.clipboard.writeText("Plain text to copy.").then(() => { // Successful copy }, () => { // Copy failed }); }); -
8:22 - Async Clipboard API plain text examples
// Programmatic copy copyButtonElement.addEventListener("click", (event) => { navigator.clipboard.writeText("Plain text to copy.").then(() => { // Successful copy }, () => { // Copy failed }); }); // Programmatic paste pasteButtonElement.addEventListener("click", (event) => { navigator.clipboard.readText().then((clipText) => { document.querySelector(".editor").innerText += clipText); }); }); -
10:25 - Web Component example markup
<template id="format-button"> <button class="format"> <span class="icon"></span> <span class="label"></span> </button> </template> -
10:36 - Registering the Web Component
let template = document.getElementById("format-button"); window.customElements.define(template.id, class extends HTMLElement { constructor() { super(); this.attachShadow({mode: "open"}); let newButtonElement = template.content.cloneNode(true); let parts = newButtonElement.querySelectorAll("span"); parts[0].textContent = this.getAttribute("data-icon"); parts[1].textContent = this.textContent; this.shadowRoot.appendChild(newButtonElement); this.addEventListener("click", this.handleClick.bind(this)); } }); -
11:02 - Web Component custom elements
<format-button id="bold" data-icon="B">Bold</format-button> <format-button id="italic" data-icon="I">Italic</format-button> <format-button id="underline" data-icon="U">Underline</format-button> <format-button id="strikethrough" data-icon="S">Strikethrough</format-button> <format-button id="paste" data-icon="📋">Paste</format-button> -
12:28 - Original example Web Component template
<template id="format-button"> <button class="format"> <span class="icon"></span> <span class="label"></span> </button> </template> -
12:30 - Example Web Component template with CSS Shadow Parts
<template id="format-button"> <button class="format"> <span part="icon" class="icon"></span> <span part="label" class="label"></span> </button> </template> -
12:38 - CSS Shadow Part styles
#bold::part(icon) { color: var(--formatting-button-icon-color); font-weight: bold; } #italic::part(icon) { color: var(--formatting-button-icon-color); font-style: italic; } #underline::part(icon) { color: var(--formatting-button-icon-color); text-decoration: underline; } -
13:16 - HTML enterkeyhint attribute
<div id="editor" contenteditable="true" enterkeyhint="send"></div> -
14:32 - System font families
font-family: system-ui; font-family: ui-sans-serif; font-family: ui-serif; font-family: ui-monospace; font-family: ui-rounded; -
14:45 - San Francisco font family
body { font-family: system-ui; font-family: ui-sans-serif; } -
14:53 - New York font family
body { font-family: ui-serif; } -
14:58 - SF Mono font family
body { font-family: ui-monospace; } -
15:03 - SF Rounded font family
body { font-family: ui-rounded; } -
16:07 - line-break: auto
code { line-break: auto; } -
16:43 - line-break: anywhere
code { line-break: anywhere; } -
17:25 - Removing margins from subsequent headings
h1, h2, h3, h4, h5, h6 { margin-top: 3em; } h1 + h2, h2 + h3, h3 + h4, h4 + h5, h5 + h6 { margin-top: 0; } -
17:56 - Removing margins from any subsequent headings
h1, h2, h3, h4, h5, h6 { margin-top: 3em; } h1 + h2, h1 + h3, h1 + h4, h1 + h5, h1 + h6, h2 + h3, h2 + h3, h2 + h4, h2 + h5, h2 + h6, h3 + h4, h3 + h3, h3 + h4, h3 + h5, h3 + h6, h4 + h5, h4 + h3, h4 + h4, h4 + h5, h4 + h6, h5 + h6, h5 + h3, h5 + h4, h5 + h5, h5 + h6 { margin-top: 0; } -
18:02 - Using :is() to remove margins from subsequent headings
h1, h2, h3, h4, h5, h6 { margin-top: 3em; } :is(h1, h2, h3, h4, h5, h6) + :is(h1, h2, h3, h4, h5, h6) { margin-top: 0; } -
18:31 - :is() specificity prevents the override from working
:is(.intro, .pullquote, #hero) + p { text-transform: uppercase; } h2 + p, h3 + p, h4 + p, h5 + p, h6 + p { text-transform: none; } -
19:07 - :where () resets specificity
:where(.intro, .pullquote, #hero) + p { text-transform: uppercase; } h2 + p, h3 + p, h4 + p, h5 + p, h6 + p { text-transform: none; } -
19:53 - WebP graceful fallback to JPG
<picture> <source srcset="example.webp" type="image/webp"> <img src="example.jpg" alt="Example Image"> </picture> -
19:54 - WebP graceful fallback to JPG and server-side detection
<picture> <source srcset="example.webp" type="image/webp"> <img src="example.jpg" alt="Example Image"> </picture> Accept: image/webp,image/png,image/svg+xml,image/*;… -
21:17 - Image with no size attributes
<img src="MexicoCity.png"> -
21:19 - Image with size attributes
<img src="MexicoCity.png" width="560" height="747"> -
21:49 - Respect EXIF image orientation default behavior
image-orientation: from-image; -
22:13 - Override image orientation to use the raw image capture
image-orientation: none; -
22:37 - HDR display CSS media query
<style> @media only screen (dynamic-range: high) { /* HDR-only CSS rules */ } </style> -
22:42 - HDR display CSS media query and JavaScript matchMedia detection
<style> @media only screen (dynamic-range: high) { /* HDR-only CSS rules */ } </style> <script> if (window.matchMedia("dynamic-range: high")) { // HDR-specific JavaScript } </script> -
23:19 - Remote Playback API example
<video id="videoElement" src="https://site.example/video.mp4"></video> <button id="deviceButton">Send video to a remote device</button> <script> let videoElement = document.getElementById("videoElement"); let deviceButton = document.getElementById("deviceButton"); deviceButton.addEventListener("click", (event) => { videoElement.remote.prompt().then(updateRemotePlaybackState); }); </script> -
24:20 - Picture in Picture example
<video id="videoElement" src="https://site.example/video.mp4"></video> <button id="pipButton">Enter picture-in-picture mode</button> <script> let videoElement = document.getElementById("videoElement"); let pipButton = document.getElementById("pipButton"); pipButton.addEventListener("click", (event) => { videoElement.requestPictureInPicture().then(handlePictureInPicture); }); </script> -
27:11 - BigInt example with division examples
let bigInt = BigInt(Number.MAX_SAFE_INTEGER); // 9007199254740991n console.log(8n / 2n); // 4n console.log(9n / 2n); // 4n -
28:02 - Nullish coalescing operator
class Person { constructor(firstName, lastName, age) { this.firstName = firstName ?? "Unknown"; this.lastName = lastName ?? "Unknown"; this.age = age ?? NaN; } } console.log(new Person()); // { firstName: "Unknown", lastName: "Unknown", age: NaN } console.log(new Person(false, false, true)); // { firstName: false, lastName: false, age: true } console.log(new Person("John", "", 0)); // { firstName: "John", lastName: "", age: 0 } console.log(new Person("John", "Appleseed", 42)); // { firstName: "John", lastName: "Appleseed", age: 42 } -
29:09 - JavaScript optional chaining example
class Person { constructor(firstName, lastName, age) { this.firstName = firstName ?? "Unknown"; this.lastName = lastName ?? "Unknown"; this.age = age ?? NaN; this.name = { firstName: this.firstName, lastName: this.lastName }; } } function register(person) { // Before optional chaining if (person !== undefined && person.name !== undefined) console.log(person.name.firstName); } register(new Person()); // undefined register(new Person("John", "Appleseed")); // "John" -
29:41 - JavaScript optional chaining example
class Person { constructor(firstName, lastName, age) { this.firstName = firstName ?? "Unknown"; this.lastName = lastName ?? "Unknown"; this.age = age ?? NaN; this.name = { firstName: this.firstName, lastName: this.lastName }; } } function register(person) { // With optional chaining console.log(person?.name.firstName); } register(new Person()); undefined register(new Person("John", "Appleseed")); "John" -
29:49 - JavaScript optional chaining with indexes
// Without optional chaining console.log(person.children[0]); // TypeError: undefined is not an object // With optional chaining console.log(person.children?.[0]); // undefined -
30:02 - JavaScript optional chaining with methods
// Without optional chaining console.log(person.fullName()); TypeError: person.fullName is not a function. // With optional chaining console.log(person.fullName?.()); undefined -
30:23 - Logical assignment operators
a &&= b // and assignment operator a ||= b // or assignment operator a ??= b // nullish assignment operator -
30:44 - Nullish coalescing approach
// Nullish coalescing approach element.innerHTML = element.innerHTML ?? "Hello World!" -
30:52 - Logical assignment operator
a &&= b // and assignment operator a ||= b // or assignment operator a ??= b // nullish assignment operator // Nullish coalescing approach element.innerHTML = element.innerHTML ?? "Hello World!" // Logical assignment operator element.innerHTML ??= "Hello World!" -
30:53 - Public class fields
class Person { firstName = ""; lastName = ""; age = NaN; children = []; constructor(firstName, lastName, age) { this.firstName = firstName ?? "Unknown"; this.lastName = lastName ?? "Unknown"; this.age = age ?? NaN; } } -
31:58 - String.prototype.replace example
"This doesn't work, and doesn't make sense".replace ("doesn't", "does"); › This does work, and doesn't make sense -
32:09 - String.prototype.replaceAll example
"This doesn't work, and doesn't make sense".replaceAll("doesn't", "does"); › This does work, and does make sense -
33:53 - App Clips banner
<meta name="apple-itunes-app" content="app-id=myAppStoreID, app-clip-bundle-id=clipBundleID, affiliate-data=myAffiliateData, app-argument=myURL">
-