Streaming is available in most browsers,
and in the Developer app.
-
What's new in Safari and WebKit
Explore the latest features in Safari and WebKit and learn how you can make better and more powerful websites. We'll take you on a tour through the latest updates to HTML, CSS enhancements, Web Inspector tooling, Web APIs, and more.
Resources
- Learn more about bug reporting
- MDN Web Docs - Web Extensions API
- Safari Release Notes
- Safari Technology Preview
- WebKit Open Source Project
Related Videos
WWDC23
- Explore media formats for the web
- What’s new in CSS
- What’s new in web apps
- What’s new in Web Inspector
WWDC22
-
Download
♪ Mellow instrumental hip-hop music ♪ ♪ Hi there! I'm Kendall Bagley, a software engineer on the Safari team.
It's been a full year since we last got together at WWDC, and today, we're going to be talking about all the amazing features and enhancements to Safari and WebKit from both what's new here at this year's WWDC and from what we've seen throughout this whole past year.
In fact, it's been quite a busy year! Since last fall, each release of Safari has delivered new and exciting features that we know y'all as web developers have been asking for.
Each of the new enhancements delivered this year aimed to address some of the biggest points of feedback you've been sharing with us.
Like adding a parent selector with the :has() pseudo class, the new flexbox inspector, and even container queries.
We want to make your daily work that much better and easier while building the best and most powerful software for the web.
In fact, what's here is just some of the new content that we'll be going over today.
But there's so much more that we wouldn't possibly be able to cover it all in this one session.
There's been a total of 162 new web platform features and improvements across the seven Safari releases this past year.
We've been proud to provide so many new tools for you to use to make your websites and web apps.
And for macOS, the best way to see what's new and exciting as soon as possible is through Safari Technology Preview, where you can try out the latest and greatest for Safari and WebKit and also help us know what we should have come next.
But like you saw, there's a whole bunch of new features, so let's take a look at everything that we're going to cover.
Today we'll be looking at new HTML features, CSS enhancements -- including a bunch to help your code architecture -- new Web Inspector tools, a wide selection of new web API, great JavaScript and WebAssembly features, and improvements to security and privacy.
So, let's get started with what's new with HTML by taking a look at a web page I'm creating for my coworkers and I to use.
I personally really like to thrift and repurpose my clothes as a way to make my wardrobe more sustainable, and I thought a clothing swap website would be a great way to have my team try it out too.
My design for the site includes a Request Item button that should show you a form to fill out when you spot a piece of clothing that you like.
And I want that form to show up in an overlay over top of the whole page.
The new dialog element provides a really easy way to create overlays in a robust and accessible manner that we can use for our request form.
And the new backdrop pseudo-element in CSS makes it possible to style the background behind the modal.
Let's bring up the dialog by requesting an item.
See that shadow and animation? It's really looking great! Then, once an item is requested on our site, the person who posted it needs to be able to accept the request.
At the bottom of the page, there's a carousel to flip through all your received requests.
But I don't want someone to accidentally interact with a button or a text field for one of the items that's not the frontmost, with either clicks or keyboard navigation.
I can use the inert attribute to fix this.
By dynamically applying the inert attribute with the JavaScript here, I'm disabling any interactions for elements on slides that aren't the currently selected slide as we switch between them.
And, using inert includes disabling interactions for assistive technologies and prevents screen readers from reading those disabled items aloud, giving much clearer guidance on which elements are intended for interaction.
And lastly for HTML, there's the new lazy loading for images.
On my site, there's some icons in the header that I need to load right away, but for the clothing item images that are offscreen on that first load, we can utilize lazy loading for them, so the images only load when the user scrolls to them, making the page feel faster and more responsive.
I'm really loving how the site's looking so far, and it's going to work great for those using assistive technologies as well.
And those HTML features are just getting us started, because there's so much to check out with CSS this year too.
A huge part of our CSS focus has been around making your CSS easier to reuse through more powerful architecture.
With that, we know the number one request for new web technology has been container queries.
And we're thrilled to announce container queries will ship in Safari 16! You'll be able to use both size queries and container query units.
Here, I'm experimenting with an alternative layout for the clothing swap website.
I'm making the card that presents a piece of clothing into a reusable component and dropping that component into several different places in the page layout.
Here in the sidebar, the available space is a bit narrow, so I want all the content inside my component to stack vertically.
In the main grid of items, I want to feature the first one as a hero graphic that should take up all the available space horizontally and arrange the content in a fashion that makes more sense for a wide layout.
The rest of the items in the main content area should be divided into smaller columns.
So I've created another layout that works when there's a medium amount of horizontal space.
Using container queries to handle the change in the layout, rather than media queries, I can write the layout code for this component just once and use that component any place on my site in a container of any size, and the correct layout will always get applied.
I specified which element to use for the container and whether I want to measure against just the inline size or both inline and block size at the same time, by using the container-type property.
I can optionally name my container using the container-name property, which gives me more flexibility in how I structure the HTML.
Then I use the @container rule to apply styles conditionally, based on the size of the container.
Here, if the clothing card component is in a container that's wider than 250 pixels, the grid will change to have two columns instead of one.
Next up with CSS architecture: cascade layers.
This is a powerful change to the CSS cascade.
Since the beginning of CSS, the cascade has been made up of these different layers.
But no matter what specificity of any given selector inside each layer, author styles -- the styles you write as web developers -- always beats UA styles.
Inline styles are always more powerful than author styles, and so on with the rest of the hierarchy.
Cascade layers takes this same concept and allows you to create your own custom layers where specificity is calculated independently inside each layer.
One entire layer beats another entire layer, no matter what the specificity is of the selectors being used.
And you determine which layer has power over the others through the order of how you define the layers in your CSS.
Cascade layers will be a useful tool for architecting CSS on large projects and maintaining that code over time.
Perhaps your team will use them to separate a design system from overrides or a framework you're using for custom styles for your project.
It's totally up to you! And, to round out all the amazing new enhancements for your CSS architecture is :has(), a pseudo-class that can act as the long-wanted parent selector and much more.
Combined with any other selector in CSS, :has() can look for siblings, attributes, states of form fields, and much more.
It's really powerful.
Here, I want to highlight the entire message box whenever someone has checked the "Urgent?" checkbox for one of their messages.
I can use the :has pseudo-class here to say that anytime the form element has an input of type checkbox with that checkbox checked, apply this CSS.
And I don't even need to use any JavaScript.
We hope all these great improvements to handling your CSS architecture, with :has(), cascade layers, and container queries, make your work as a web developer that much better.
But these aren't the only CSS additions that we're excited about.
You've wanted a tool similar to existing viewport units but would be more useful on devices where scrolling causes the size of the viewport to change.
And for that, there's new viewport units for y'all.
When you want to know the height of the viewport when it's at its smallest, use svh.
For the height of the viewport when it's at its largest, use lvh.
Just remember: s for small, l for large.
For a dynamic number that changes to always match the current actual height of the viewport, use dvh.
And it's not just for height.
We've got you covered with even more viewport units.
There's width units, which are good for completeness to match up with the highly used height units.
We've got block and inline -- both being useful when writing for multiple languages with differing ways in which text can flow.
And we didn't forget, min and max, too.
But what about when you want to create some movement on your page, not just react to it? Animation has previously been very declarative where you can specify a start, an end, and a duration to get objects moving.
But it's been a challenge to animate elements on a page either when trying to get it to follow a curved path or even just being able to move it around by an offset.
And I'd like to add a secret animation for the header when you click on it, Really just thought it'd be fun.
With the new offset-path, you can define a path that you want your object to animate along.
Set the path with offset-path and use offset-distance for the keyframe effect.
Then use the animation property to apply the keyframe effect, giving you all the control you'd want with your animations, all in CSS.
We also want to give you more control over your page even with the parts of the web that have typically been defined by the browser engine itself, and over scroll-behavior is just our first example of this.
Since the beginning of the web, if you click on a link that moves you to another part of a web page, it visually appears as a jump.
Sometimes this is disorienting to your users.
The scroll-behavior property in CSS provides a way to specify if you want this behavior or not.
By default, it's set to auto, and it'll appear as that jump.
By specifying scroll-behavior as smooth, you can ask the browser to instead scroll smoothly to the next place on the page.
You can also do this with the JavaScript methods window.scroll(), scrollTo(), or scrollBy().
You know your customers best and should be able to define your own web page experience outside of the browser engine defaults, which is also where the use of :focus-visible as well as accent-color can come into play.
You're probably familiar with the focus selector if you've ever wanted to apply a specific style to the focus indicator, likely to have it more in line with your overall design.
But there are some accessibility pitfalls of losing the browser-based heuristic when you do that.
And on my site, instead of the built-in form colors, I'd love to use a custom color.
Let's use the teal color that's already in my header for both the focus highlight and the checkbox.
With the :focus-visible pseudo-class, you can style the focus indicator how you choose while also having that stylized indicator only show if it would be shown natively by the browser.
And to add another layer of customization to your forms, you can use accent-color to change the color of different parts of the form control UI.
It'll take affect on that checkbox as well as radio buttons, and so much more.
Also with CSS, we've been replacing more and more of the WebKit prefixes.
These used to be the perfect way to try out experimental features, but now, we're able to move towards their standards-defined properties to make your CSS easier to write and more interoperable.
But don't worry, your existing CSS with WebKit prefixes will keep working as you transition to their web standards counterparts.
Backface-visibility, print-color-adjust, and text-align: match-parent are all exactly the same as their prefixed counterpart.
Both mask and text-combine-upright have had their syntax updated from the prefixed version to match the standard.
And the nonprefixed appearance property also adds support for the new auto value but has removed the WebKit-specific values in Safari 16, like caret or listitem, as it got brought up to standards specifications.
There's been a lot to note about our typography additions as well.
In particular, we've added the font-palette property that allows for easy selection of a color palette within a color font.
It's something that I think would be really cool to try out with some potential logos for my site.
We can test out how it looks with the built-in dark or light palettes or even what it'd be like to customize it to exactly what I want with color overrides and get some yellow in there to brighten it up.
And with typography, there's been the addition of text-decoration-skip-ink, which allows you to control what happens when an underline or overline intersects with a letter or character.
Plus the ic unit, which makes it possible to precisely line up CJK characters in the block direction.
It's useful for creating a clean typography grid in languages like Chinese, Japanese, and Korean.
To wrap up our discussion of all these great CSS features, we've definitely got to talk about subgrid.
For years, layout on the web was pretty hard.
CSS Grid has been revolutionary, but it only affects the direct children of a grid container.
Here, I'm using CSS Grid to layout these cards, and to automatically adjust the layout to fit the viewport width by adding and removing columns without any media queries.
But the size of the content on each card isn't the same; some headlines are longer, the photos have different aspect ratios, and that's causing the visuals to look really messy.
I'd like for all the Request Item buttons and the message boxes to line up across the page, and I'd like a longer title on one card to affect the layout on the other cards, so they all get the same spacing.
Now, we can accomplish this using subgrid.
I've put a grid on each article, and I've tied all of those grids to the grid of their parent simply by writing "grid-template-rows: subgrid." You can see how all the content on each clothing card now perfectly lines up by using the Grid Inspector in Web Inspector where I can turn on all the grids I could possibly need too.
A lot of CSS work becomes easier when we use the Web Inspector.
In fact, there's been some amazing additions to the Web Inspector that I think you'll be really excited to try out.
First off, layout is easier to write when you can see what's going on, which is exactly what makes the Web Inspector so important.
And with the new Flexbox Inspector, you can actually visualize the spacing between elements.
Here on my website, I was having some trouble adding these icons to my header.
All I need to do is inspect the element, and go to the Layout tab, and since I'm not concerned with my grids right now, I can go ahead and collapse that section to get right to the new Flexbox Inspector.
I can even turn on all the views with just a single click and still have smooth performance.
And with all the views turned on, I can clearly see with the hash marks and container boxes how my elements are being arranged and how the empty space is taking up the view.
So now I want to make sure I'm getting my alignment right, which I can use the new alignment editor for.
I can go to the Styles tab to find a new button next to align-items.
Here, I'm able to toggle through the different options to find what works best for my header, and I can do the same with justify-content as well.
Again, just toggling through each of the options and then landing on the one that I think looks just right.
I also think the yellow icons are a bit too small, and I want to try making them the same size as the red icons, which I believe are using a variable with "medium" in the name, but I really can't remember the full name.
I can try out changing the size by inspecting one of the yellow icons and editing its height in the inspector.
And, thanks to our new CSS fuzzy autocompletion, I can go ahead and type "medium" and the variable I want pops up even though "medium" is at the end of the name.
And those yellow icons definitely aren't too small anymore.
And when those other variables for the different icons aren't being used for the element I'm inspecting, they get hidden away with our new CSS tooling.
But don't worry, there's a button to reveal them when you need them.
And probably most excitingly for Web Inspector this year, we are happy to announce support for developer tool extensions for the Safari Web Inspector.
The creators of your favorite developer tools extensions will now be able to port them to Safari, using the same underlying APIs that they use in other browsers.
If you're interested in learning how to make an extension for the Web Inspector, exploring the new APIs, and getting set up to start using them yourself, make sure to watch "Create Safari Web Inspector Extensions" at this year's WWDC.
Now we've covered a lot of what's new with our front-end technologies, so let's switch gears and get into what's new with our web APIs.
Which we are so excited to announce support for web push.
It'll be available in Safari 16 on macOS Ventura.
It's coming to iOS and iPadOS next year.
Web push lets you remotely send notifications to your users from your website or web app.
This is a fully interoperable, standards-based implementation.
If you've implemented web push already and it works in other browsers, it should just work in Safari without any modifications.
And you don't need an Apple Developer account either.
To learn all about the details, watch "Meet Web Push for Safari" here at WWDC22.
If you're excited about web push, then you'll probably be excited about new web app manifest improvements too.
Now, you can define the icon that's used when people save your web app to the Home Screen in your manifest file.
To have the icons in the manifest take precedence, you'll need to ensure that there is no apple-touch-icon defined in the HTML head.
If you want to deliver one icon to iOS and iPadOS, while delivering a different icon to other mobile platforms, you can still do so by defining the icon for Apple devices in that HTML head using the apple-touch-icon.
And if you don't declare an icon in either place, then when a user saves your site to the Home Screen, they'll simply get a screenshot of your site.
Excitingly, we also no longer wait for the user to choose "Add to Home Screen" from the Share menu to load the manifest file, which means you can use that manifest file to define characteristics of your web page on all your sites and even further reduce the need to use meta tags.
Continuing with our APIs, we've done a lot to improve the use of web pages in multiple browsing contexts with the same origins.
Broadcast channels allow you to send notifications between those different browsing contexts.
Let's imagine someone is using the clothing swap website and they have it open in two windows at the same time.
Then they claim a piece of clothing in one window.
We'll be able to post a message and sync that unavailable state to any other open tabs or windows.
But maybe it's not updating a tab in the background, but updating a file saved for your site.
For that, there's been the addition of the File System Access API.
We've had incremental updates to this API across multiple releases this year, starting with origin private file system, which is private storage based on origin.
So for instance, my clothing swap site wouldn't have other sites, like apple.com, reading its files.
We then added to the API with the getFile() method of FileSystemFileHandle, which reads an existing file retrieved from your site's root directory, like we're doing with a draft file here that we also happened to have just created.
Now let's take a look at our most vibrant API addition this year with some new color richness.
The Display P3 color space makes it possible to represent colors that just don't exist in RGB.
Here, we've got some examples of the color picker.
On the left of the squiggly white line is color that exists in RGB.
And on the right of the line are colors only available in P3.
In 2016, we added P3 support for videos and photos.
Last year, we were thrilled to be the first browser engine to implement the new color syntax defined in CSS Color Level 4.
This year, we've added support for P3 color for content inside the canvas element.
So, no need to use the colors based on devices all the way from the 90s, when you can now start utilizing the full color capabilities of all the amazing devices of today.
But there's even more to check out with our new Web APIs from this past year, including shadow realms, web locks, and updated support to the ResizeObserver API for the ResizeObserverSize interface, which will help you observe changes to an element's box-sizing properties.
There's so much to try out across all of our new API additions, and of course, with all of our new features too.
In fact, we've still got more to cover.
So let's next get into all that's new in JavaScript & WebAssembly.
If your website uses workers, and you want instances of these workers to be shared across tabs and windows, then the new shared workers interface will definitely help and potentially reduce memory usage.
Instead of spawning new workers for every task that you want to happen in the background, you can have just one worker in use for each browsing context with the same origin.
Each script would create a shared worker in the same way, and then they can receive and post messages using the same port.
The shared worker would be able to receive and respond to messages sent from all of the different scripts.
This will result in less demand on your servers, while also making your webpage fast and responsive for your customers.
We've also got a whole array of array features to show you.
Instead of having to mutate an array using reverse() when you want to search from the end, you now can use the findLast() and findLastIndex() methods, like I've done here to find the item and index for the last item containing a "shoestring." The new at() method also makes searching from the end of an array even easier.
Using braces works great when the index is positive, but with at(), we get the additional feature of indexing with negative values making your code more concise and readable.
But even with that good number of new array features, nothing much can beat the sheer number of new internationalization features we got for you.
WebKit has continued to add regular updates to our Intl implementation throughout this past year.
There's been added support for different numbering systems with new methods in NumberFormat, calendars, thanks to updates with Locale as well as DisplayNames, and currency with the Intl Enumeration API.
And like I said, there's a lot that's been added to our Intl implementation this year that you'll have no shortage of things to try out and explore to cater to your users across the world.
And for all those that have existing code in all sorts of different coding languages, like C, Objective C, or Swift, that they'd like to bring to the web, WebAssembly gets them running without any need to rewrite.
And with this year's improvements, your web apps using WebAssembly are only getting more powerful with the addressable memory being expanded to 4GB, and the performance gains that come with the new zero-cost exception handling.
Overall, there's definitely some exciting stuff for JavaScript and WebAssembly to try out here.
And speaking of WebAssembly, we also have some security and privacy enhancements that not only will protect the users of the web who we develop for, but will also bring new potential for you as developers.
With both of the new Cross Origin Opener Policy and Cross Origin Embedder Policy HTTP response headers, your site can opt in to process isolation, which means your site will run in its own dedicated webContent process.
We know that a lot of apps can benefit from running on multiple threads in parallel using WebAssembly threading, and with these new headers, you're able to do so securely.
Our second security enhancement also involves HTTP headers with our improved support for content security policy level 3.
CSP provides enhanced security control over your loading content and mitigates risk of cross-site scripting and other vulnerabilities.
With the level 3 updates, the most exciting addition has been the new strict-dynamic source expression.
The designers of strict-dynamic realized you can use nonces to allow certain scripts, then extend that trust to scripts loaded by the already trusted ones.
No explicit allow list needed.
Look how much simpler the header becomes.
Going from that original long list of domains that could potentially end up allowing too much.
And with that, we wrap up our security and privacy features, which also brings us to the end of all that we'll get to cover today, but there's even more to explore on your own.
For instance, we've had media updates including support for capturing a specific Safari window with the getUserDisplay() API, WebRTC Perfect Negotiation, In-band chapter tracks, and requestVideoFrameCallback().
As well as a lot of cool additions for web extensions with manifest version 3 support, and a bunch of new web extensions APIs.
To dive deeper into all these features covered here today, and to explore all the 162 features and improvements developed in Safari and WebKit in the past year, make sure to download Safari Technology Preview to keep up with what's coming in the future, explore web technology by checking out our release notes, blog posts, and all the great content on webkit.org, including extensive documentation for Web Inspector.
And as always, let us know what you think and what you'd like to see next by filing your bug reports.
If you come across a bug in WebKit -- something about HTML, CSS, JavaScript, DOM APIs, or the Web Inspector -- make sure to send your feedback through WebKit's bug tracking system at bugs.webkit.org.
And for suggestions or bugs with the Safari interface, file issues in Apple's Feedback Assistant.
We look forward to delivering more of the amazing features that make the work of web developers like you that much better with all the Safari and Safari Technology Preview releases to come in this next year.
Thank you for joining me today, and I hope you have the best time here at WWDC.
Bye now! ♪
-
-
2:59 - Dialog element
<!-- <dialog> element --> <dialog method="dialog"> <form id="dialogForm"> <label for="givenName">Given name:</label> <input class="focus" type="text" name="givenName"> <label for="familyName">Family name:</label> <input class="focus" type="text" name="familyName"> <label> <input type="checkbox"> Can trade in person </label> <button>Send</button> </form> </dialog>
-
3:09 - Backdrop pseudo-class
/* ::backdrop pseudo-element */ dialog::backdrop { background: linear-gradient(rgba(233, 182, 76, 0.7), rgba(103, 12, 0, 0.6)); animation: fade-in 0.5s; } @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
-
3:53 - inert attribute
// inert attribute function switchToIndex(index) { this.items.forEach(item => item.inert = true); this.items[index].inert = false; this.currentIndex = index; }
-
4:22 - Lazy image loading
<img src="images/shirt.jpg" loading="lazy" alt="a brown polo shirt" width="500" height="600">
-
6:46 - Container Queries
/* Container queries */ .container { container-type: inline-size; container-name: clothing-card; } .content { display: grid; grid-template-rows: 1fr; gap: 1rem; } @container clothing-card (width > 250px) { .content { grid-template-columns: 1fr 1fr; } /* additional layout code */ }
-
8:05 - Cascade layers
/* Author Styles - Layer A */ @layer utilities { div { background-color: red; } } /* Author Styles - Layer B */ @layer customizations { div { background-color: teal; } } /* Author Styles - Layer C */ @layer userDefaults { div { background-color: yellow; } }
-
8:54 - :has() pseudo-class
<!-- :has() pseudo-class --> <style> form:has(input[type="checkbox"]:checked) { background: #ff927a; } </style> <form class="message"> <textarea rows="5" cols="60" name="text" placeholder="Enter text"></textarea> <div class="checkbox"> <input type="checkbox" value="urgent"> <label>Urgent?</label> </div> <button>Send Message</button> </form>
-
11:08 - Offset Path
/* offset-path */ :is(.blue, .teal, .yellow, .red) { offset-path: circle(9vw at 5vw 50%); } @keyframes move { 100% { offset-distance: 100%; } } /* Animation */ .clothing-header.clicked :is(.blue, .teal, .red, .yellow) { animation: move 1100ms ease-in-out; }
-
11:43 - scroll-behavior: auto
html { scroll-behavior: auto; }
-
12:09 - scroll-behavior: smooth
html { scroll-behavior: smooth; }
-
13:10 - :focus-visible & accent-color
/* :focus-visible & accent-color */ :focus-visible { outline: 4px solid var(--green); box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3); } :root { accent-color: var(--green); }
-
14:50 - Font palette dark mode & light mode
/* Dark mode */ font-palette: dark; /* Light mode */ font-palette: light;
-
15:01 - Font palette custom colors
/* Dark mode */ font-palette: dark; /* Light mode */ font-palette: light; /* Custom colors */ @font-palette-values --MyPalette { override-colors: 1 yellow; } #logo { font-palette: --MyPalette; }
-
15:55 - CSS Grid
/* Grid to layout cards */ main { display: grid; grid-template-columns: repeat(auto-fit, minmax(225px, 1fr)); gap: 1rem; } /* Grid to layout each card’s content */ article { display: grid; grid-row: span 5; }
-
16:35 - Adding sub grid
/* Grid to layout cards */ main { display: grid; grid-template-columns: repeat(auto-fit, minmax(225px, 1fr)); gap: 1rem; } /* Grid to layout each card’s content */ article { display: grid; grid-row: span 5; /* Adding subgrid, tying them together */ grid-template-rows: subgrid; }
-
21:15 - Web App Manifest file icons
// Manifest file "icons": [ { "src": "orange-icon.png", "sizes": "120x120", "type": "image/png" } ]
-
21:29 - apple-touch-icon
<!-- HTML head --> <link rel="apple-touch-icon" href="blue-icon.png" />
-
22:36 - Broadcast Channel
// State change broadcastChannel.postMessage("Item is unavailable");
-
23:14 - Origin private file system
// Accessing the origin private file system const root = await navigator.storage.getDirectory(); // Create a file named Draft.txt under root directory const draftHandle = await root.getFileHandle("Draft.txt", { "create": true }); // Access and read an existing file const existingHandle = await root.getFileHandle("Draft.txt"); const existingFile = await existingHandle.getFile();
-
25:32 - Shared Worker
// Create Shared Worker let worker = new SharedWorker("SharedWorker.js"); // Listen for messages from Shared Worker worker.port.addEventListener("message", function(event) { console.log("Message received from worker: " + event); }); // Send messages to Shared Worker worker.port.postMessage("Send message to worker");
-
25:56 - findLast() and findLastIndex()
const list = ["shirt","pants","shoes","hat","shoestring","dress"]; const hasShoeString = (string) => string.includes("shoe"); console.log(list.findLast(hasAppString)); // shoestring console.log(list.findLastIndex(hasAppString)); // 4
-
26:17 - at()
const list = ["shirt","pants","shoes","hat","shoestring","dress"]; // Instead of this: console.log(list[list.length - 2]); // It's as easy as: console.log(list.at(-2));
-
29:12 - strict-dynamic source expression
// strict-dynamic source expression // Without strict-dynamic Content-Security-Policy: script-src desired-script.com dependent-script-1.com dependent-script-2.com dependent-script-3.com; default-src "self"; // With strict-dynamic Content-Security-Policy: default-src "self"; script-src "nonce-desired" "strict-dynamic";
-
-
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.