스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
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.
리소스
관련 비디오
WWDC20
-
다운로드
Hello and welcome to WWDC Hello I'm Jon Davis, Web Technologies evangelist for the Safari and WebKit teams.
I'm so excited to get to tell you about all of the new features and improvements for web developers this year. If you develop websites, web apps save to the home screen, or Web content used in apps, and want to learn about all of the latest technologies available in Safari and WebKit, this session is for you. In this session. I'll give you a tour of the new features and enhancements in WebKit and shipping in Safari 13.1 for macOS, Safari on iOS and iPadOS 13.4 and coming to Safari 14 on macOS, iOS an iPadOS. But first, there's some really great news I want to share up front. We made a really big effort this year to improve Safari's interoperability with other browsers. One way to measure that is web platform tests. It's a set of tests used by browser makers to ensure interoperability, so your web content operates the way you expect across browsers. So I'm pleased to announce improved interoperability for service workers, XHR + Fetch, pointer events, CSS, SVG, web assembly, and many more areas. Safari passes a hundred and forty thousand new interoperability test cases this year. And you can look forward to continued progress in this area. But there's so much more to talk about this year. In this session, I'm going to go over performance improvements, lots of new web API, CSS updates, media enhancements, including some great image updates, new JavaScript features, and finally, new platform integration capabilities. So let's get started with performance and specifically, browsing performance. Page load performance is foundational to our browsing experience feeling fast and I'm pleased to report Safari 14 is 13 percent faster, clicking a link to an unvisited site, and up to 42 percent faster clicking a link for recently visited web page. Typing a URL into the search and address bar for a recently visited web page is 52 percent faster. We improved instant back by caching up to 34 percent more pages. PFDs show the first page 60 times faster while downloading. And outside of page load performance, closing unresponsive tabs is down from three and a half seconds to just fifty milliseconds. All of these improvements help users and developers have a faster browsing experience in Safari. And there's performance improvements for developers too. CPU usage while scrolling is three times less. It's buttery smooth with 0 dropped frames. IndexedDB operations are up to 10 times faster. Reduced overhead makes for-of loops of the five times faster based on micro benchmarks. Promises are twice as fast in the jet stream to benchmarks asyncfs test, and optimize JavaScript delete operations are up to twelve times faster. Safari and WebKit are faster than ever. But now I'm going to show you the new Web API added in WebKit and available in Safari. First up, is the Web Animations API. It's long awaited and new in Safari 13.1. It's an API available in JavaScript to directly create and control the playback of CSS animations and transitions. You no longer need to manipulate element properties. And you can query the animations on the page. Seek directly to a specific time in the animation playback cycle or even change the speed and direction of playback. So I've been experimenting with a loading animation of the WebKit logo for an internal website where my colleagues enjoy discussing their pets, and after it's animated in the compass needle spins and it's a really fun element. So I'd like to let folks interact with it a bit by clicking on the logo to give the needle another spin. And with the web animations API it's really easy. So in the logos click handler we use the element.animate method. It takes a keyframe effect parameter that allows us to setup the keyframes for the animation, a nice easing function so the needle starts spinning quickly with a softer ending and then a reasonable duration in milliseconds. And here it is. And clicking the logo keeps the needle spinning. That's more fun than I thought it'd be.
And Web Inspectors Graphics tab now shows you the animation instances.
Including the animations created each time I click the logo. It also visualizes the easing curves and to help with timing, it gives you a perspective of the animation delay compared to its duration. There are lots of updates to Web Inspector you can learn about the What's New in Web Inspector session available in your Developer app. So that's the Web animations API. JavaScript control of animations with all of the power and efficiency of CSS animations and transitions. Next in our tour is ResizeObserver. It's newly available with the release of Safari 13.1 earlier this year. It's a JavaScript API that reports element size changes. It allows elements to respond to the size changes of other elements not just the viewport. With ResizeObserver your content can react to elements that change their size both when the viewport changes like when the window gets resized but more importantly when other changes to size occur, like changing the display property or when new child elements are appended.
My colleagues on the WebKit team love discussing their pets, so I've been maintaining an internal website where they can share pictures and comment on them. And of course it uses responsive design so that when you resize the window and things start to get a little cramped, the control shrink to just show the icon and save space. But some folks said they wanted a flexible editor because they like having the extra space while commenting. You can see it resizes well for the window but it doesn't work when resizing the editor. This hasn't been easily achieved before but ResizeObserver is a great tool for this because it can detect when the size of the container changes and react. Let me show you what I've been working on. So to address this, I'll create a new ResizeObserver in the site's JavaScript that detects when the containers width goes below a certain size then toggle a CSS class on the container. The style rules for our formatting buttons can pick that up and hide the label to use less space and collapse the size of the button so they always fit. To get it working. I just passed the container element to the observer method of my newly constructed ResizeObserver. Let's check it out. With the code in place, the resizing the editor causes the buttons to hide the labels and resize the way you'd expect.
That's ResizeObserver. If you want to learn more, there's a blog post on the WebKit blog with more details. Another feature I need to get working on the Webkitten's website is the paste button, and I've got just the thing. The async clipboard API. It's also new this year and available in Safari 13.1. You can read data from the system clipboard for paste operations or write data to the clipboard for copying. It's asynchronous to avoid blocking the page while you're accessing the clipboard. And there's no need to fake a selection or have element focus to copy data into it. It supports multiple items and item types like images and rich formatted text but, needs a secure context over https and calls must be invoked in response to user interaction.
Now if you're working with just plain text it has shortcut methods to make it even easier. Copying plain text is as easy as using the clipboard right text method to write a string of text. And pasting plain text, uses of the clipboard read text method. But for the WebKittens website the team wants rich HTML formatted text when pasting. Inside the click handler the clipboard read method is called asynchronously to get an array of clipboard items back. The clipboard items are iterated to find the HTML data.
The data gets returned as a blob object. Then file reader is used to get the HTML data back which is collected and appended to the editor.
I copied a comment from another post earlier to test with so I'll click the paste button and post it. And there you have it, a functional paste button that handles rich text. The async clipboard API is powerful and this just scratched the surface. You can learn more about it on the WebKit blog.
Now if you're a developer of a library or framework, this next news is for you.
Safari 13.1 now supports the EventTarget constructor. EventTarget is used by objects that can receive events. For example, Dom elements extend the EventTarget. But now your own objects can too without all the extra element behaviors. Library authors can use native event functionality to create their own object interface for dispatching custom events for non-dom objects. While we're talking about library developers a lot of libraries are simply providing a custom component for web authors to implement. So that leads us to an update on web components.
We've had support for web components in Safari for a long while now and they keep becoming more and more powerful. It's actually another feature I took advantage of on the WebKitten's website.
Everything from the posts, to the comments, and even the formatting buttons are implemented as web components, But for a simple example let's look specifically at the formatting buttons. Some simple template markup is used to setup the component as a custom element. The ID attribute is used to reference this template in JavaScript. Then in JavaScript it's registered as a custom element. The ID attribute value is repurposed to serve as the custom element hook.
Our custom element class extends a generic HTML element and in the constructor clones the template content to modify the template fragment with our text label and icon. And then appends our modified DOM fragment to the shadow root of the custom element. Then the page markup can create those elements using the custom elements registered name as the tag name.
Each of the format buttons can be customized for bold, italic, underline, and so on. Now as a component author you provide a generic component that can get used in many ways so you want to give the page authors some control over how these elements are style. In this case as a component author we don't know what kind of button each one will end up being. In the example, component styling handles the layout of a larger text icon to the left of the button label. But in order to give the bold buttons B icon a bold style and an italic style for the I icon the page author who knows what kind of button they're implementing needs to be able to customize that part of the component.
With Safari 13.1 released this spring they can, using CSS shadow parts. It allows web component authors to specifically expose parts of their components to content authors to style with their own CSS. You don't need to know all of the underlying markup structure of the component to style it, just the parts of the component the author exposed through the part attribute. Component layout is protected and content authors can customize the components to fit the use case or better match the style of their website. Looking back at our example, this was the original template markup. By adding the part attribute to the elements inside the component they're now exposed to the pages CSS. Now the page author that's implementing the buttons can use the part pseudo element selector and easily decorate the buttons to provide an extra visual hint about their function. So this, turns into this. And that's CSS shadow parts for web components. Speaking of visual hints, WebKit added support for another visual hint. The HTML enter key hint attribute it supported on Safari on iOS 13.4 and iPadOS. It allows you to declare an action label for the enter key on virtual keyboards of a touchscreen device. You can set the label to give your users a hint about what action the enter key will take. Instead of just enter it could be done or go or send, for example. Before I wrap up the new Web API, there's one more API to mention, the web authentication API. It supports logging into websites beyond usernames and passwords, and it was introduced in Safari 13 and Safari on iOS 13.3 with support for hardware security keys. And with the latest Safari, WebKit has added support for Touch ID and Face ID in Safari on macOS iOS and iPadOS. You can learn all about the security and convenience of implementing it for your users by watching the Face ID and Touch ID on the web session in the Developer app.
And that's a look at the new Web API in Safari this year: web animations ResizeObserver, the async clipboard API, The EventTarget Constructor,CSS Shadow Parts, HTML. Enter Key Hints and web authentication. I can't wait to see what you do with these new capabilities. But we're not done yet.
Let's take a look at several CSS improvements that give content authors more fine grained control over styles and layout.
This year WebKit added support for system font families. They work in WebKit across all of Apple's platforms. They each map to a system appropriate font.
The system UI font family is a generic alias for UI sans serif and on the system it maps to San Francisco. UI serif uses the New York font family.
UI monospace uses SF Mono. And UI rounded uses SF rounded. They're useful when developing a web app that you want to make feel more familiar to the system and the different font families allow you to create an easily identifiable difference between content and user interface, such as on the WebKit website. It uses UI serif for the content areas And UI sans serif for the user interface when adding a comment. So beautiful new font families for your web apps. Another CSS feature that can help your content layout is support for line break anywhere. It breaks to a new line at any opportunity before the content overflows. This can be particularly helpful with long words that can overflow narrow containers, especially technical jargon like code where hyphenation might change the intended meaning of the word. And it can protect your content from unexpected layout issues. The simplest way to understand it is to see the behavior of the default line break rule. WebKit uses a default line break heuristic that's based on language specific rules while taking into account other CSS that might apply. With Roman-based written languages, the line break heuristic hyphenates the text when it can, but take a look at the first word on this page from the web inspector command line API reference. The long query instances syntax doesn't break at all. It's breaking right before the long word making a blank line after the bullet. Then it still overflows the container and the viewport. Line break anywhere makes it possible to fix this.
With line break anywhere, it breaks T the character just before the overflow making it possible to see all of the content without breaking the layout. Now it's possible to see the entire line. Next up is another powerful CSS tool, the is pseudo-selector. It's newly supported in Safari 14. It matches a list of selectors just like the matches pseudo-selector. In fact is aliases our matches pseudo- selector behavior that's been part of Safari for years. The matching element gets the specificity of the most specific selector. It's really useful for avoiding repetitive selectors. Here's an example. Here a 3em top margin is added to all the headings. Then this rule removes the top margin when a heading is immediately followed by a heading of the next level down. But, of course, if this is used in a content management system, page authors may not adhere to strict rules of an h1 followed by an h2. They're not technically prevented from using an h1 followed by an h3, so this isn't enough to cover all of those cases. To do that you'd need a really repetitive selector like this. That's pretty awful looking. But with is we can simplify writing all of that out and it becomes this. That's so much nicer. To go along with is WebKit also supports the where pseudo-selector. It works the same way as is in that it matches a list of selectors, but the big difference is that the CSS specificity of any matching element is always zero. So it can act as a kind of specificity reset. Here's another example. And I'll start by comparing is first. Looking at this example the is selects an intro class, pull quote, or element with the hero ID and styles the paragraph tag that immediately follows it, to use uppercase text and give it an eye-grabbing look. Later in the styles the page author is trying to specifically override the heading levels two through six followed by a paragraph to use normal text. Seems like they only want this to work for paragraphs following level 1 headings. But using is means that it won't work as expected. This is where the where pseudo-selector comes in. Using where instead makes this possible. Now, the elements matched by where a reset to a specificity level of zero making the follow up rules able to override as expected.
From CSS we move on to media. But included in media is a fair number of image updates as well. And I'm pleased to announce support for an entirely new image format webP images.
WebP is an open source image format that provides smaller file sizes and lots of advance bells and whistles. It supports a lossy format comparable to JPEG and a lossless format like PDG. It even supports transparency and animation across both. In your markup you can use the picture elements to add webP images with a fallback and on the server side you can look at the accept header. But a big reason web developers are excited for this format is the file size savings. This sample JPEG encoded at 80 percent quality is 5.1 megabytes but the webP lossy encoding of the same quality settings gives us a 41 percent file savings with visual quality that's nearly the same as a JPEG. This high resolution PNG weighs in at eight hundred and seventeen kilobytes but the lossless webP encoding preserves the transparency and saves us 33 percent. WebP image support is available in Safari 14 and Safari for iOS 14. While we're talking about images there are a couple of new default image behaviors in WebKit. The first is a change that will improve the way your web pages load. We've all seen what happens when we load a web page where the images load in and cause the layout to jump around.
I'm going to show you how easy it is to fix this with WebKits new behavior for calculating the default image aspect ratio. In Safari 13.1 and in Safari on iOS 13.4, WebKit now calculates the image aspect ratio from an image tags width and height attributes. All you have to do is make sure to add the width and height attributes to your image tags. Let's see what happens when the attributes are added.
There's no jumping around at all. The space is reserved for the image and it just loads right in. Beautiful. The other new default behavior is for image orientation. We've supported respecting EXIF image orientation on iOS for a long time. This update aligns our support on iOS and macOS through the standard image orientation from image value. The from image value tell Safari to respect the image orientation flag encoded into images that support EXIF data. This JPEG image is encoded with an EXIF orientation flag of 6 meaning the camera was rotated 90 degrees counterclockwise. And now you can override this default by setting image orientation value to none to display the image directly as encoded without rotation correction. From images we move on to video. With system support for high dynamic range videos in web content with Safari 14 on macOS.
You can use media queries to detect high dynamic range display support. In CSS you can query support with dynamic range high like this. Or you can use the windows matchMedia method in JavaScript so you can deliver progressively enhanced content to users with HDR displays. Continuing with video updates WebKit has added support for the remote playback API. We've always had an API for doing AirPlay before but the remote playback API is a standards based way of adding remote playback of audio or video to your custom web based media player and sending it to a variety of other remote playback devices like connected TVs, audio-only speakers, and any AirPlay capable devices. To use it, you'll set up a custom button on your video player controls and in response to user interaction call The video elements remote prompt method. Then, you can handle updating remote playback state in your callback handler. It's really that easy. Then on your device, users can tap on the control to get a menu of available remote playback devices. When selected the video is sent to that device. Supporting the remote playback API gives your users the flexibility to enjoy media on all of their devices. And another way to help users enjoy your media is the picture in picture API.
Adding a picture in picture control to your web player allows users to play videos in a pop-out window while they continue doing other tasks. Like remote playback, WebKit has supported picture in picture for a while but the standards based picture in picture API is now available and it works across iOS, iPadOS and macOS. Similar to using the remote playback API you set up a custom control element and call the video elements request picture in picture method in response to user interaction. That's it. Then your users can enjoy videos in picture in picture mode on their device while doing other tasks. In other news related to video, Safari 14 include support for timed metadata in HLS. It's new in Safari 14. And this is metadata synchronized to the media timestamps in the video stream. It can be used to provide program information like episode details or live sports data like inning boundaries or scores. There are two approaches supported, The HLS EXT daterange tag is available in the data queue and carries the metadata to the HLS media playlists themselves. The advantages of using these tags is their immediacy and scope. All metadata in media playlists is available to the video player as soon as it's loaded. It doesn't require waiting for the specific segment to load or for the playhead to pass over it.
And event message boxes are now available in fragmented MP4s. These are compatible with ID3 storage and other formats like MPEG-2 transport streams to carry the same metadata in a fragmented MP4 containers. This is especially important for codecs like HEVC and Dolby Atmos which Apple platforms only recognize inside fragmented MP4s. The last update is an enhancement for developers of video sites that provide subtitles and captioning.
This is a short but sweet update. It's an enhancement to text track you that allows you to use your own captions format but use native caption rendering.
Developers can use existing caption formats without needing to convert them all. And because the user agent is responsible for displaying the captions, they get first class accessibility treatment. Plus they render in both full screen and picture in picture without any extra work. Those are our media updates with support for webP images, new defaults for image aspect ratio and orientation, Mac HDR video, timed metadata in HLS, remote playback, and picture in picture APIs with natively rendered subtitles in captions. Now it's time for new JavaScript features. Starting with BigInts. BigInts are new data types in JavaScript. They're integers that are arbitrarily large and most commonly used in cryptography, but are useful wherever you need numbers larger than the max safe integer in JavaScript. But there are a few things to keep in mind. You can't use number operations or mix operations with the regular number data type. You also can't use JSON.stringifty to serialize a BinInt for backwards compatibility reasons. You'll need to build your own serialization functionality to ensure the decoding process matches your serialization format.
And be aware that when using the division operator, BigInts will drop any decimal values. That's BinInts available in Safari 14. But WebKit also supports some powerful new operators to help with null and undefined values.
The nullish coalescing operator works like other logical operators but it checks for existence. Let's look at a quick example.
So I have a person class where the constructor takes a first name, last name, and age argument. The nullish coalescing operator is used to check if an argument's value is provided. If it's null or undefined, the result of the right-hand side of the operator is used instead.
Here without any arguments the right-hand side defaults as I like to think of them, are used. But passing boolean values, regardless of what they are, pass the existence check and are set as the properties and the person object.
Passing a filled in string or empty string or even zero for the age still evaluates to set the fields to the passed in arguments. And finally passing the full data sets the properties and that's nullish coalescing. Building on nullish coalescing and also new this year is optional chaining. It's a new JavaScript syntax that gives you a shortcut for property access. And it even works for indexes and methods too. Take a look at this example.
Here, we add a name property to include both the first and last name. And when a person is registered we want to see the first name. This is how you might typically add some guards to make sure you can access the first name property.
Passing no arguments to the person when calling register gives us an undefined result.
And passing strings for the first name and last name gives us the expected first name, but with optional chaining, we can replace this approach with new syntax that uses the optional chaining operator. And it works in the exact same way. It also works with indexes like checking for the first entry in an array of a person's children. Using optional chaining here avoids the type error and it even works for methods. Trying to call an undeclared method triggers an error but with optional chaining you can avoid the error. So that's optional chaining. Sticking with the operators theme they're even more newly available operators in Safari 14, Logical assignment operators. And they come in three flavors: logical and assignment, logical or assignment, plus a knowledge assignment operator. Logical assignment operators are nice in that they only evaluate the left-hand side expression once and it isn't as destructive in that it doesn't always reassign. Take this example using the nullish coalescing operator where it always reassigns inner HTML. With a knowledge assignment operator, it only destructively assigns inner HTML if it is null or undefined. Switching gears back to objects for a moment, WebKit in Safari 14 include support for public class fields. Public class fields are new syntax to declare member properties that are part of the object, regardless of them being set up in the constructor. Looking at the person example earlier they could be set up like this. Now, children, for example, will still be available when a new person object is created, even though it isn't set in the constructor. By not having it in the constructor you can focus on the logic instead of worrying if variables are set, as public class fields are guaranteed to always be added. And a final utility in JavaScript to look at is String replaceAll. Finally, String replaceAll is new in Safari 13.1 and it does what it promises, replacing all instances of a string. Previously the string replace method would only replace the first occurrence of the string. You'd have to iterate over the words in the string or use a global regular expression.
With replaceAll you can replace all the instances of the string. And those are the powerful new JavaScript features available in Safari this year: BigInt, Nullish coalescing, optional chaining, Logical assignment operators, Public class fields, and String replaceAll. That's our JavaScript updates.
And finally I'm going to share a few exciting platform integration capabilities new this year.
First up is AR quick look on iOS. These are experiences launched from Safari that can be customized with Web technologies. And new with iOS 13.3, these experiences can include a banner for users to buy products. You can customize individual elements of the banner or provide a completely custom banner with a simple HTML page. To learn more I encourage you to check out the Shop Online with AR Quick Look session in the Developer app.
Another platform technology in Safari updated this year is Apple Pay.
Apple Pay on the web has been updated to add new button types with custom rounded corners and the ability to request redacted billing details.
This is great for merchants that calculate tax based on a customer's location but have no use for the shipping address. To learn more see the What's New in Wallet and Apple Pay session in the Developer app. Finally another experience new this year is app clips. It gives users a focused, fast, and frictionless experience without waiting for downloading and installing an entire app. On your website you can let users know that an app clip interaction is available by adding the Apple iTunes meta tag to your websites HTML and include your App Store ID and the new app clip bundle ID parameter to add a banner to your website. You can learn more about app clips by watching the Explore App Clips session in the Developer app.
That wraps up our platform integration with customizable AR quick look, Apple Pay updates, and the all new app clips experience. And it also wraps up our tour of the new features and improvements of Safari and WebKit.
There's a whole lot new in Safari this year powered by changes in WebKit and I encourage you to try these features out and share your feedback. Now is the perfect time to test it and we'd love to hear how it goes especially if you run into bugs. For Web API, CSS, or JavaScript bugs you can file issues on the Web get bug tracker at bugs.webkit.org. For Safari bugs or issues specific to iOS, home screen web apps, or Apple platform integration features, you can use feedback assistant. If you want to stay on the leading edge of what's coming you should download and use Safari Technology Preview. It's a separate version of Safari that works right alongside of Safari and it's shipped every two weeks with a sneak peek of new and experimental features as well as new web inspector tools. So as a web developer you can be using the latest developer tools every two weeks. You can also stay informed by keeping tabs on the WebKit website at webkit.org. It features blog posts, safari technology preview, release notes, and a fantastic library of reference guides for using web inspector. You can also follow WebKit on Twitter for new announcements about the WebKit project along with tips and tricks for web inspector. And check out the links related to this session including Safari Web Extensions. What's New in Web Inspector and more web technologies. Thanks for joining me and enjoy WWDC.
-
-
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">
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.