νλ°νΈμλ κ°λ°μ κ°μ₯ λ§μ μν₯μ μ£Όλ ν¬λ‘¬ λΈλΌμ°μ μ λ²μ λ³ λ³κ²½ μμ νλͺ©μ μ 리 λ° κ³΅μ νλ€.
π‘ κ° νλͺ©μ Chrome Platform Statusμ Roadmapκ³Ό ν λ¬κ°μ blink-dev νλ μμ½μ λ°νμΌλ‘ μ 리νλ€.
π‘ κ° νλͺ©μ π«λ μ§μ μ€λ¨ λ° μ κ±°(Removed), β οΈλ μ§μ μ€λ¨(Deprecated), β λ μλ‘μ΄ κΈ°λ₯(Enabled by default)λ₯Ό μλ―Ένλ€.
π‘ κ° νλͺ© μ€ κΈ°μ‘΄ μλΉμ€μ λ―ΈμΉλ μν₯μ΄ ν¬λ€κ³ νλ¨ν νλͺ©μ μμ λͺ© λ€μ π νμλ₯Ό νλ€.
π‘ μ§μ μ€λ¨ λ° μ κ±°(π«), μ§μ μ€λ¨(β οΈ) μΈμ νλͺ©μ 곡μ κ°μΉκ° μλ€κ³ νλ¨ν κ²½μ°μλ§ ν¬ν¨νλ€.
π‘ κ° νλͺ©μ λν μ£Όμ λΈλΌμ°μ λ° μΉ κ°λ°μμ μ견μ Chrome Platform Statusλ₯Ό κ·Έλλ‘ μΈμ©νλ€.
document.domain
μ€μ μ(Setter) μ§μ μ€λ¨ πshadowroot
μμ± μ§μ μ€λ¨ 미리 보기FormData
μμ±μμ submitter
μΈμ μΆκ°v
νλκ·Έ μΆκ°CollectedClientAdditionalPaymentData
μ rp
μμ± μ΄λ¦ λ³κ²½Headers.getSetCookie()
λ©μλ μΆκ°RTCPeerConnection.getStats()
μ½λ°± λ°©μ μ§μ μ€λ¨ 미리 보기 πdisplay
, content-visibility
μ μ λλ©μ΄μ
μ¬μ© μ§μshadowroot
μμ± μ§μ μ€λ¨RTCPeerConnection.getStats()
μ½λ°± λ°©μ μ§μ μ€λ¨ πdocument.domain
μ€μ μ(Setter) μ§μ μ€λ¨ πSOP(Same-Origin Policy)λ λΈλΌμ°μ 보μ μ μ±
μΌλ‘, λ νμ΄μ§κ° μλ‘ λ€λ₯Έ μΆμ²(origin)μΌ λ μ΄ λ νμ΄μ§ μ¬μ΄μ ν΅μ μ λ§λλ€. μλ₯Ό λ€μ΄ parent.example.com
νμ΄μ§μ <iframe>
μΌλ‘ child.example.com
νμ΄μ§λ₯Ό λΆλ¬μ€λ©΄ κΈ°λ³Έμ μΌλ‘ child.example.com
μ window.parent
λ‘ λΆλͺ¨μ°½μ μ κ·Όν μ μκ³ , μ΄ λ°λλ λ§μ°¬κ°μ§λ€. κΈ°μ‘΄μλ μμ λλ©μΈμ΄ κ°μ κ²½μ°, document.domain
μ€μ μλ₯Ό μ¬μ©νμ¬ μμ λλ©μΈ(example.com
)μΌλ‘ μΆμ²λ₯Ό λ³κ²½ν΄ SOPλ₯Ό μνν μ μμλ€. λ³΄λ€ κ°λ ₯ν 보μμ μν΄ μ΄ μ€μ μλ₯Ό μ§μ μ€λ¨νλ€. μ§μ μ€λ¨ κΈ°κ° λμ document.domain
μ€μ μλ₯Ό νΈμΆν μλ μμ§λ§ μΆμ²λ λ³νμ§ μλλ€.
μ¬μ©μκ° μ§μ chrome://flags
μμ Origin-keyed agent clusters
κΈ°λ₯μ μ€μ ν΄ κΈ°μ‘΄μ²λΌ document.domain
μΌλ‘ μΆμ²λ₯Ό λ³κ²½ν μ μλ€. νμ§λ§ κΈ°λ³Έ μ΅μ
μ΄ μλκΈ°μ λμμ μ°Ύλ κ²μ΄ μ’λ€. κ°μ₯ κ°λ¨νκ² μλ‘ λ€λ₯Έ μΆμ² κ° ν΅μ μ window.postMessage
λ‘ κ΅¬νν μ μλ€.
// parent.example.com
window.addEventListener('message', (event) => {
if (event.origin !== 'http://child.example.com') {
return;
}
console.log(event.data); // μλ
νμΈμ?
});
// child.example.com
window.postMessage('μλ
νμΈμ?', 'http://parent.example.com');
window.postMessage
μ μμΈν μ¬μ©λ² λ° λ€λ₯Έ λμμ μ¬κΈ°λ₯Ό μ°Έκ³ ν΄λ μ’λ€.
μ΄ νλͺ©μ λν μ£Όμ λΈλΌμ°μ λ° μΉ κ°λ°μμ μ견μ λ€μκ³Ό κ°λ€.
document.domain
setter.shadowroot
μμ± μ§μ μ€λ¨ 미리 보기μ μΈμ Shadow DOMμ <template>
μμμ shadowroot
μμ±μ μ§μ ν΄ ν
νλ¦Ώμμ μ§μ Shadow DOMμ νμ±νν μ μλ κΈ°λ₯μ΄λ€. νμ€μμ μ΄ μμ±μ λν μ΄λ¦μ shadowrootmode
λ‘ λ³κ²½ν¨μ λ°λΌ κΈ°μ‘΄ shadowroot
μμ±μ μ§μ μ€λ¨ν μμ μ΄λ€. shadowroot
λ ν¬λ‘¬μμ κ·Έλλ‘ λμνλ, Chrome 111μμ μΆκ°λ μ€νΈλ¦¬λ° κΈ°λ₯μ μ¬μ©ν μ μλ€. shadowrootmode
λ Chrome 111 μ΄μμμ μ¬μ©ν μ μμΌλ©°, λ€λ₯Έ λΈλΌμ°μ μμλ μ§μ μμ μ΄λ―λ‘ shadowrootmode
μμ±μ μ¬μ©νλ κ²μ΄ μ’λ€.
<host-element>
- <template shadowroot="open">
+ <template shadowrootmode="open">
<slot></slot>
</template>
<h2>Light content</h2>
</host-element>
shadowroot
μμ±μ μ€λ 10μ λ§ λ°°ν¬ μμ μΈ Chrome 119μμ μ§μ μ€λ¨ μμ μ΄λ€. Chrome 112μμλ shadowroot
μμ± μ§μ μ€λ¨μ λν 미리 λ³΄κΈ°λ§ μΆκ°νλ€. chrome://flags
μμ ν΄λΉ κΈ°λ₯μ νμ±νν΄ μμ± μ§μ μ€λ¨μ 미리 체νν μ μλ€.
μ΄ νλͺ©μ λν μ£Όμ λΈλΌμ°μ λ° μΉ κ°λ°μμ μ견μ λ€μκ³Ό κ°λ€.
shadowroot
attribute for declarative shadow DOM (Deprecated)FormData
μμ±μμ submitter
μΈμ μΆκ°FormData
μμ±μμ λ λ²μ§Έ μ ν μΈμλ‘ submitter
λ₯Ό μΆκ°νλ€. submitter
λ λμ <form>
μμμμ submit
μν μ νλ μμλ‘, <button type=submit>
μ΄ λνμ μ΄λ€. FormData
μΈμ€ν΄μ€ μμ± μ μ΄ submitter
μΈμλ₯Ό λκΈ°λ©΄ ν΄λΉ μμμ κ°μ ν¬ν¨ν FormData
μΈμ€ν΄μ€λ₯Ό μμ±νλ€.
<form id="myForm" action="/api">
<input name="title" value="μ¬λ¦ ν΄μμ§λ₯Ό μ νν΄μ£ΌμΈμ." />
<button name="type" value="beach">λ°λ€</button>
<button name="type" value="mountain">μ°</button>
<button name="type" value="valley">κ³κ³‘</button>
</form>
μ <form>
μ μ΄λ€ λ²νΌμ λλ λμ§μ λ°λΌ μλ²(/api
)μ 보λ΄λ κ°μ΄ λ¬λΌμ§λ€. λ§μ½ λ°λ€
λ²νΌμ λλ λ€λ©΄ μλ²κ° λ°λ κ°μ μλμ κ°λ€.
title=μ¬λ¦ ν΄μμ§λ₯Ό μ νν΄μ£ΌμΈμ.
type=beach
νμ§λ§ μ€ν¬λ¦½νΈμμ μ λ°μ΄ν°μ κ°μ FormData
λ₯Ό μμ±νλ €λ©΄ μ§μ κ°μ μΆκ°ν΄μΌ νλ€.
document.getElementById('myForm').addEventListener('submit', (event) => {
event.preventDefault();
const formData = new FormData(event.target);
// βΈ FormData(1) { title -> "μ¬λ¦ ν΄μμ§λ₯Ό μ νν΄μ£ΌμΈμ." }
formData.append(event.submitter.name, event.submitter.value);
});
Chrome 112λΆν°λ FormData
μ submitter
μΈμλ₯Ό λκΈ°λ©΄ submitter
μ κ°μ ν¬ν¨ν FormData
μΈμ€ν΄μ€λ₯Ό μμ±νλ€.
document.getElementById('myForm').addEventListener('submit', (event) => {
event.preventDefault();
const formData = new FormData(event.target, event.submitter);
// βΈ FormData(2) { title -> "μ¬λ¦ ν΄μμ§λ₯Ό μ νν΄μ£ΌμΈμ.", type -> "beach" }
});
SubmitEvent.submitterλ Chrome 81 μ΄μμμ μ¬μ©ν μ μλ€. μ΄ νλͺ©μ λν ν΄λ¦¬νμ΄ μμΌλ μ€μ μ μ© μ μ°Έκ³ νμ.
μ΄ νλͺ©μ λν μ£Όμ λΈλΌμ°μ λ° μΉ κ°λ°μμ μ견μ λ€μκ³Ό κ°λ€.
CSS κ·μΉμ μ€μ²© μ μν μ μλ κΈ°λ₯μ μΆκ°νλ€. Scss λ¬Έλ²κ³Ό λΉμ·νλ, κ·Όλ³Έμ μΈ μ°¨μ΄κ° μλ€. μλ₯Ό λ€μ΄ Scssλ μμ μ νμ, &
λ₯Ό λ¬Έμμ΄λ‘ λ³Έλ€.
.foo {
&Bar {
color: red;
}
}
μ νμΌμ Scss μμ§μΌλ‘ λ³ννλ©΄ λ€μκ³Ό κ°μ κ²°κ³Όκ° λμ¨λ€.
.fooBar {
color: red;
}
νμ§λ§ CSSμ μ€μ²© λͺ¨λμ &
λ₯Ό λ³λμ μ»΄ν¬λνΈλ‘ λ³Έλ€. λ°λΌμ 첫 λ²μ§Έ μμλ₯Ό μλμ²λΌ ν΄μνλ€.
Bar.foo {
color: red;
}
CSS μ€μ²© λͺ¨λμ μμ μμλ₯Ό .foo
λ¬Έμμ΄κ³Ό Bar
λ¬Έμμ΄μ ν©μΉ κ² μλ .foo
ν΄λμ€ μ νμμ Bar
νμ
μ νμλ₯Ό ν©μΉ κ²μΌλ‘ λ³Έλ€.
λν μ€μ²© ꡬ문μ νμ μ νμλ ν¨μ ꡬ문μΌλ‘ μμν μ μλ€.
/* μ¬λ°λ₯΄μ§ μμ CSS ꡬ문! */
li {
color: lime;
input {
margin-top: 1em;
}
}
μ΄ κΈ°λ₯μ μμ§ μ΄μμ΄κΈ° λλ¬Έμ μ€μ ꡬν κ³Όμ μμ κ·μΉμ΄ λ³κ²½λ μ μλ€. μμΈν κ·μΉμ 곡μ λ¬Έμλ₯Ό μ°Έκ³ νμ.
μ΄ νλͺ©μ λν μ£Όμ λΈλΌμ°μ λ° μΉ κ°λ°μμ μ견μ λ€μκ³Ό κ°λ€.
v
νλκ·Έ μΆκ°μ κ·μμ μ λμ½λ μ§ν© λͺ¨λ(/v
)λ₯Ό μΆκ°νλ€. v
νλκ·Έλ ES2015 μ λμ½λ λͺ¨λ(/u
)μ νμ₯νμΌλ‘, u
νλκ·Έλ³΄λ€ λ λ§μ κΈ°λ₯μ μ§μνλ€.
λ¨Όμ u
νλκ·Έκ° νλμ μ½λ ν¬μΈνΈ(code point)λ§ μ§μνλ κ²κ³Ό λ¬λ¦¬ v
νλκ·Έλ λ€μμ μ½λ ν¬μΈνΈλ₯Ό μ§μνλ€.
{
// u νλκ·Έ
const regex = /^\p{RGI_Emoji}$/u;
regex.test('β½'); // '\u26BD', ν κ°μ μ½λ ν¬μΈνΈ
// -> true
regex.test('π¨πΎββοΈ'); // '\u{1F468}\u{1F3FE}\u200D\u2695\uFE0F', 5κ°μ μ½λ ν¬μΈνΈλ‘ μ΄λ£¨μ΄μ Έ u νλκ·Έλ‘λ κ²μ¬ν μ μλ€.
//-> false
}
{
// v νλκ·Έ
const regex = /^\p{RGI_Emoji}$/v;
regex.test('β½'); // '\u26BD'
// -> true
regex.test('π¨πΎββοΈ'); // '\u{1F468}\u{1F3FE}\u200D\u2695\uFE0F'
// -> true
}
λν μ λμ½λ μμ± μμΈ νν(\p
)κ³Ό μ λμ½λ μ§ν© ννμμ ν¨κ» μ¬μ©ν μ μλ€.
// νκΈμ΄λ©°, "γ΄"μ΄ μλ μ λμ½λ λ¬Έμ
/[\p{Script_Extensions=Hangul}--γ΄]/v.test('γ΄'); // -> false
// νκΈμ΄λ©°, "γ±", "γ΄", "γ·"μ΄ μλ μ λμ½λ λ¬Έμ
/[\p{Script_Extensions=Hangul}--[γ±-γ·]]/v.test('γ΄'); // -> false
--
λ₯Ό μ¬μ©νλ©΄ νΉμ μ λμ½λ μμ±μ κ°μ§ λ¬Έμ μ€μμ μ§μ ν λ¬Έμ λλ λ¬Έμ μ§ν©μ μ μΈν μ μλ€.
&&
λ κ΅μ°¨ ꡬ문μΌλ‘ λ μ λμ½λ μμ±μ λͺ¨λ κ°μ§ μ λμ½λ λ¬Έμλ₯Ό κ°λ¦¬ν¨λ€.
// κ·Έλ¦¬μ€ λ¬Έμμ΄λ©°, λ¬ΈμμΈ μ λμ½λ λ¬Έμ
const regex = /[\p{Script_Extensions=Greek}&&\p{Letter}]/v;
// U+03C0 κ·Έλ¦¬μ€ PI μλ¬Έμ
regex.test('Ο'); // true
// U+1018A κ·Έλ¦¬μ€ 0μ ν΄λΉνλ μ«μ
regex.test('π'); // false
u
νλκ·Έλ μ λμ½λ μμ±λΌλ¦¬λ§ μ§ν©μ μμ±ν μ μμ§λ§, v
νλκ·Έλ λ¬Έμμ΄κ³Όλ ν¨κ» μ§ν©μ μμ±ν μ μλ€.
// νκΈ λλ 0-9 μ¬μ΄μ μλΌλΉμ μ«μ
const regex = /^[\p{Script_Extensions=Hangul}0-9]$/v;
regex.test('μ€λ¬Όλμ΄'); // true
regex.test('22μ΄'); // true
μ΄ νλͺ©μ λν μ£Όμ λΈλΌμ°μ λ° μΉ κ°λ°μμ μ견μ λ€μκ³Ό κ°λ€.
Chrome 112μ 2023λ 3μ 29μΌμ μ μ λ°°ν¬ μμ μ΄λ€. 3μ 9μΌλΆν° 16μΌ μ¬μ΄μ ν¬λ‘¬ λ² ν λ²μ μμ ν΄λΉ κΈ°λ₯μ 미리 νμΈν΄ λ³Ό μ μλ€.
CollectedClientAdditionalPaymentData
μ rp
μμ± μ΄λ¦ λ³κ²½SPC(Secure Payment Confirmation)λ κ²°μ κ±°λ μ€ κ°μνλ μΈμ¦ μ μ°¨λ₯Ό μ 곡νλ μΉ APIμ΄λ€. WebAuthnμ κΈ°λ°μΌλ‘ λ§λ€μμΌλ©°, WebAuthnμμ CollectedClientAdditionalPaymentData
μ rp
(Relying Party, μ λ’° λΉμμ¬) μμ±μ μ΄λ¦μ rpId
λ‘ λ³κ²½ν¨μ λ°λΌ νμ€μμλ rp
λ₯Ό rpId
λ‘ λ³κ²½νμλ€. λ³κ²½λ νμ€ λ¬Έμμ λ°μμ μν΄ rp
μμ±μ μ κ±°νλ€.
Chrome 107μμ λ¨Όμ CollectedClientAdditionalPaymentData
μ rpId
μμ±μ μΆκ°νμΌλ―λ‘ rp
λμ rpId
λ₯Ό μ¬μ©νλλ‘ λ³κ²½ν΄μΌ νλ€.
μ΄ νλͺ©μ λν μ£Όμ λΈλΌμ°μ λ° μΉ κ°λ°μμ μ견μ λ€μκ³Ό κ°λ€.
Headers.getSetCookie()
λ©μλ μΆκ°HTTP μμ²μ λ³΄λΌ λ Set-Cookie
ν€λλ μ¬λ¬ λ² μ€λ³΅μΌλ‘ μ μΈν μ μμΌλ©°, μλμΌλ‘ ν©μ³μ§μ§ μλλ€. νμ§λ§ νμ¬ Headers
λ λ±λ‘ν Set-Cookie
ν€λλ₯Ό λ°λ‘ κ°μ Έμ¬ μ μλ κΈ°λ₯μ μ 곡νμ§ μλλ€. μμλ‘ νμ¬ Headers.get('Set-Cookie')
λ λͺ¨λ Set-Cookie
ν€λμ κ°μ ν©μΉ κ²°κ³Όλ₯Ό λ°ννλ€.
const a = new Headers();
a.append('Set-Cookie', 'a=1');
a.append('Set-Cookie', 'b=2');
console.log(a.get('Set-Cookie')); // a=1, b=2
HTTPμ κ·μΉκ³Ό λμΌνκ² Headers
μ Set-Cookie
λ₯Ό λ°°μ΄λ‘ λ°ννλ Headers.getSetCookie()
λ©μλλ₯Ό μΆκ°νλ€. μΌλΆ λΈλΌμ°μ λ Headers
κ°μ²΄λ₯Ό μνν λ Set-Cookie
λ₯Ό ν©μΉμ§ μλ κΈ°λ₯λ λ¨Όμ μ μ©νλ€.
μ΄ νλͺ©μ λν μ£Όμ λΈλΌμ°μ λ° μΉ κ°λ°μμ μ견μ λ€μκ³Ό κ°λ€.
RTCPeerConnection.getStats()
μ½λ°± λ°©μ μ§μ μ€λ¨ 미리 보기 πRTCPeerConnection.getStats()
λ ν΄λΉ μ€λμ€λ λΉλμ€ μ°κ²°μ λν ν΅κ³ μλ£λ₯Ό λ°ννλ APIλ€. νμ¬ ν¬λ‘¬μμ μ΄ APIλ νλ‘λ―Έμ€λ₯Ό λ°ννλ μ΅μ λ²μ κ³Ό, μ½λ°±μ μ¬μ©νλ ꡬλ²μ λ κ°μ§λ‘ λλλ€.
myPeerConnection.getStats(); // μ΅μ λ²μ
myPeerConnection.getStats(null, onSuccess, onFailure); // ꡬλ²μ , Promise λ°ν X
μ½λ°± λ°©μμ λͺ¨λ μλ°μ€ν¬λ¦½νΈμ μ΄μΈλ¦¬μ§ μκ³ , νμ€ μ€ν λ¬Έμλ μμ΄ μ₯κΈ°μ μΌλ‘ μ§μμ μ€λ¨ν μμ μ΄λ€. Chrome 113μμλ μ½λ°± λ°©μ μ¬μ© μ μ½μμ κ²½κ³ λ₯Ό λ
ΈμΆνλ©°, chrome://flags
μμ μ§μ μ€λ¨μ 체νν μ μλ κΈ°λ₯μ μΆκ°ν μμ μ΄λ€.
μ½λ°± λ°©μμ μ€λ 10μ λ§μ μ μ λ°°ν¬ μμ μΈ Chrome 119μμ μ§μ μ€λ¨ μμ μ΄λ€.
μ΄ νλͺ©μ λν μ£Όμ λΈλΌμ°μ λ° μΉ κ°λ°μμ μ견μ λ€μκ³Ό κ°λ€.
Chrome 113μ 2023λ 4μ 26μΌμ μ μ λ°°ν¬ μμ μ΄λ€. 4μ 6μΌλΆν° 13μΌ μ¬μ΄μ ν¬λ‘¬ λ² ν λ²μ μμ ν΄λΉ κΈ°λ₯μ 미리 νμΈν΄ λ³Ό μ μλ€.
Popoverλ μΉ νμ΄μ§ μμ λ§μμ°λ UIλ₯Ό λ§νλ€. Date Picker, Context Menu, κ°λ¨ν ν΄ν λ±, μΉ κ°λ°μμ Popoverλ₯Ό μ¬μ©νλ μλ μμ£Ό λ§λ€. νμ§λ§ μν κ΄λ¦¬, ν¬μ»€μ€ κ΄λ¦¬, μ κ·Όμ± λ± κ³ λ €ν μ¬νμ΄ λ§μ μκ·Όν ꡬνμ΄ μ½μ§ μμ κΈ°λ₯ μ€ νλλ€.
Chrome 114μμ μΆκ° μμ μΈ HTML μμμ popover
μμ±μ μ¬μ©νλ©΄ μ΄ Popoverλ₯Ό μ½κ² ꡬνν μ μλ€.
<div popover>μλ
νμΈμ?</div>
popover
μμ±μ μ§μ ν μ μλ κ°μ "auto" | "manual"
μ΄λ©°, κΈ°λ³Έκ°μ "auto"
μ΄λ€. λ κ°μ μ°¨μ΄λ λ€μκ³Ό κ°λ€.
"auto"
:
popover
μμλ₯Ό μ¨κΈ΄λ€.popover
μμ λ°κΉ₯μ ν΄λ¦νκ±°λ, escape λ²νΌμ λ루λ μ±κ²©μ λμ μ νμ¬ popover
μμλ₯Ό μ¨κΈ΄λ€."manual"
:
popover
μμλ₯Ό μ¨κΈ°μ§ μλλ€.popover
μμλ κΈ°λ³Έμ μΌλ‘ position: fixed;
μ€νμΌμ κ°μ§λ€. <dialog>
μ²λΌ ::backdrop
κ°μ μ νμλ‘ λ°°κ²½ μμμ μ κ·Όν μ μμΌλ©°, :open
μΌλ‘ νλ©΄μ λνλ popover
μμλ₯Ό, :closed
λ‘ μ¨μ popover
μμλ₯Ό μ νν μ μλ€.
[popover]::backdrop {
background-color: rgba(0, 0, 0, 0.6);
}
[popover]:open {
display: flex;
}
popovertarget
, popovertargetaction
μμ±μ μ¬μ©νλ©΄ μλ°μ€ν¬λ¦½νΈ μμ΄λ popover
μμλ₯Ό μ μ΄ν μ μλ€.
<span id="myPopover" popover>μλ
νμΈμ?</span>
<button type="button" popovertarget="myPopover">
μ΄ λ²νΌμ λλ₯΄λ©΄ λ©μμ§κ° λνλκ±°λ μ¬λΌμ§λλ€.
</button>
<button type="button" popovertarget="myPopover" popovertargetaction="show">
μ΄ λ²νΌμ λλ₯΄λ©΄ λ©μμ§κ° λνλ©λλ€.
</button>
<button type="button" popovertarget="myPopover" popovertargetaction="hide">
μ΄ λ²νΌμ λλ₯΄λ©΄ λ©μμ§κ° μ¬λΌμ§λλ€.
</button>
<button type="button" popovertarget="myPopover" popovertargetaction="toggle">
μ΄ λ²νΌμ λλ₯΄λ©΄ λ©μμ§κ° λνλκ±°λ μ¬λΌμ§λλ€.
</button>
λλ μλ°μ€ν¬λ¦½νΈλ₯Ό ν΅ν΄ μ§μ popover
μμμ μνλ₯Ό μ μ΄ν μλ μλ€.
const popover = document.getElementById('myPopover');
popover.showPopover();
console.log(popover.matches(':open')); // true
popover.hidePopover();
console.log(popover.matches(':open')); // false
popover.togglePopover();
console.log(popover.matches(':open')); // true
popover
μμ±μ <dialog>
μ λΉν΄ μ’ λ λ²μ©μ μ΄λ€. HTML νκ·Έκ° μλ μμ±μ΄κΈ° λλ¬Έμ μ맨ν±(sementic) μΉμ ꡬμΆνκΈ° μ’μΌλ©°, μμνκ² CSSλ‘ μ λλ©μ΄μ
μ ꡬνν μλ μκ³ , <dialog>
μ open
μ΄λ²€νΈκ° μλ κ²κ³Ό λ¬λ¦¬ νλ©΄μ λνλ λλ μ΄λ²€νΈ(beforetoggle
)λ₯Ό λ°μμν¨λ€.
λ€λ§ popover
λ λΈλΌμ°μ κ° μ§μ display
μ€νμΌμ μ‘°μν΄ μνλ₯Ό μ μ΄νλ―λ‘ μ λλ©μ΄μ
μ ꡬνν λ μ£Όμν΄μΌ νλ€. display
μμ±μ κ΄ν μ λλ©μ΄μ
λ° transition
μ§μμ΄ Chrome 114μ κ°μ΄ λ°°ν¬λ μμ μ΄λ μ°Έκ³ νμ.
μ΄ νλͺ©μ λν μ£Όμ λΈλΌμ°μ λ° μΉ κ°λ°μμ μ견μ λ€μκ³Ό κ°λ€.
display
, content-visibility
μ μ λλ©μ΄μ
μ¬μ© μ§μdisplay
μμ± λ° content-visibility
μμ±μ CSS μ λλ©μ΄μ
μμ μ¬μ©ν μ μλλ‘ λ³κ²½νλ€.
@keyframes slideOut {
from {
display: block;
}
to {
transform: translateX(40px);
opacity: 0;
}
}
.hide {
animation: slideOut 0.2s;
display: none;
}
μ μμμμ hide
ν΄λμ€λ₯Ό μ§μ ν μμλ μ λλ©μ΄μ
μμ΄ λ°λ‘ νλ©΄μμ μ¬λΌμ§λ€. display
μμ±μ opacity
λ±μ μμ±κ³Ό λ¬λ¦¬ μ€κ° κ°μ μ°μ°ν μ μλ λΆμ°μ(discrete
) μμ±μ΄λ€. μ λλ©μ΄μ
μμ display
μμ± μ체λ₯Ό μ§μνμ§ μμ display: block;
ꡬ문μ 무μν κ²μ΄λ€.
Chrome 114λΆν°λ display
λ° content-visibility
μμ±μ μ€κ° κ°μ none | hidden
μ΄ μλ νΉμ κ°μΌλ‘ μ°μ°ν΄ ν΄λΉ μμ±μ μ λλ©μ΄μ
μ¬μ©μ μ§μν μμ μ΄λ€.
μ΄ νλͺ©μ λν μ£Όμ λΈλΌμ°μ λ° μΉ κ°λ°μμ μ견μ λ€μκ³Ό κ°λ€.
CSS λΆμ°μ(discrete
) μμ±μ΄λ μ€κ° κ°μ μ°μ°ν μ μλ display
, visibility
λ±μ λ§νλ€. μ΄λ€μ μ€κ° κ°μ μ°μ°ν μ μμ΄ transition
μμ±κ³Ό ν¨κ» μ¬μ©ν μ μμλ€.
κΈ°μ‘΄μλ νΉμ μ λλ©μ΄μ
μ΄ λλ ν display: none;
μμ±μ μ μ©νλ €λ©΄ μλ°μ€ν¬λ¦½νΈλ‘ μ§μ μ€νμΌμ λ³κ²½ν΄μΌ νλ€.
.hide {
transition: opacity 0.3s;
opacity: 0;
/* display: none; */
}
const target = document.querySelector('#target');
target.addEventListener('transitionend', () => {
target.style.setProperty('display', 'none');
});
target.classList.add('.hide');
Chrome 114λΆν°λ transition-property
μμ±μ λΆμ°μ μμ±μ λͺ
μν΄ transition
μ λμμΌλ‘ μ§μ ν μ μλ€.
.hide {
transition: opacity 0.3s, display 0.3s;
opacity: 0;
display: none;
}
const target = document.querySelector('#target');
target.classList.add('.hide');
μ£Όμν΄μΌ ν μ μ all
μ μ¬μ ν λΆμ°μ μμ±μ ν¬ν¨νμ§ μλλ€λ μ μ΄λ€. λΆμ°μ μμ±μ transition
μ λμμΌλ‘ μ§μ νλ €λ©΄ ν΄λΉ μμ±μ λͺ
μμ μΌλ‘ transition-property
μ μμ±ν΄μΌ νλ€.
/* μ λλ©μ΄μ
μμ΄ λ°λ‘ μ¬λΌμ§λ€. */
.hide {
transition: all 0.3s;
opacity: 0;
display: none;
}
/* 0.3μ΄ ν μ¬λΌμ§λ€. */
.hide {
transition: display 0.3s, opacity 0.3s;
opacity: 0;
display: none;
}
μ΄ νλͺ©μ λν μ£Όμ λΈλΌμ°μ λ° μΉ κ°λ°μμ μ견μ λ€μκ³Ό κ°λ€.
Chrome 114μ 2023λ 5μ 24μΌμ μ μ λ°°ν¬ μμ μ΄λ€. 5μ 4μΌλΆν° 11μΌ μ¬μ΄μ ν¬λ‘¬ λ² ν λ²μ μμ ν΄λΉ κΈ°λ₯μ 미리 νμΈν΄ λ³Ό μ μλ€.
shadowroot
μμ± μ§μ μ€λ¨μ μΈμ Shadow DOMμ <template>
μμμ shadowroot
μμ±μ μ§μ ν΄ ν
νλ¦Ώμμ μ§μ Shadow DOMμ νμ±νν μ μλ κΈ°λ₯μ΄λ€. νμ€μμ μ΄ μμ±μ λν μ΄λ¦μ shadowrootmode
λ‘ λ³κ²½ν¨μ λ°λΌ κΈ°μ‘΄ shadowroot
μμ±μ μ§μ μ€λ¨νλ€. shadowroot
λ ν¬λ‘¬μμ κ·Έλλ‘ λμνλ, Chrome 111μμ μΆκ°λ μ€νΈλ¦¬λ° κΈ°λ₯μ μ¬μ©ν μ μλ€. shadowrootmode
λ Chrome 111 μ΄μμμ μ¬μ©ν μ μμΌλ©°, λ€λ₯Έ λΈλΌμ°μ μμλ μ§μ μμ μ΄λ―λ‘ shadowrootmode
μμ±μ μ¬μ©νλ κ²μ΄ μ’λ€.
<host-element>
- <template shadowroot="open">
+ <template shadowrootmode="open">
<slot></slot>
</template>
<h2>Light content</h2>
</host-element>
Chrome 112μμ shadowroot
μμ±μ λν μ§μ μ€λ¨ 미리 보기λ₯Ό μΆκ°ν μμ μ΄λ€. λ°°ν¬ ν chrome://flags
μμ ν΄λΉ κΈ°λ₯μ νμ±νν΄ μμ± μ§μ μ€λ¨μ 미리 체νν μ μλ€.
μ΄ νλͺ©μ λν μ£Όμ λΈλΌμ°μ λ° μΉ κ°λ°μμ μ견μ λ€μκ³Ό κ°λ€.
shadowroot
attribute for declarative shadow DOM (Deprecated)RTCPeerConnection.getStats()
μ½λ°± λ°©μ μ§μ μ€λ¨ πRTCPeerConnection.getStats()
λ ν΄λΉ μ€λμ€λ λΉλμ€ μ°κ²°μ λν ν΅κ³ μλ£λ₯Ό λ°ννλ APIλ€. νμ¬ ν¬λ‘¬μμ μ΄ APIλ νλ‘λ―Έμ€λ₯Ό λ°ννλ μ΅μ λ²μ κ³Ό, μ½λ°±μ μ¬μ©νλ ꡬλ²μ λ κ°μ§λ‘ λλλ€.
myPeerConnection.getStats(); // μ΅μ λ²μ , Promise λ°ν
myPeerConnection.getStats(null, onSuccess, onFailure); // ꡬλ²μ , Promise λ°ννμ§ μμ
μ½λ°± λ°©μμ λͺ¨λ μλ°μ€ν¬λ¦½νΈμ μ΄μΈλ¦¬μ§ μκ³ , νμ€ μ€ν λ¬Έμλ μμ΄ μ₯κΈ°μ μΌλ‘ μ§μμ μ€λ¨ν μμ μ΄λ€.
Chrome 113μμλ μ½λ°± λ°©μ μ¬μ© μ μ½μμ κ²½κ³ λ₯Ό λ
ΈμΆνλ©°, chrome://flags
μμ μ§μ μ€λ¨μ 체νν μ μλ κΈ°λ₯μ μΆκ°ν μμ μ΄λ€.
μ΄ νλͺ©μ λν μ£Όμ λΈλΌμ°μ λ° μΉ κ°λ°μμ μ견μ λ€μκ³Ό κ°λ€.
λ΄λΆλ§ μ κ·Ό κ·μΉ(Local Network Access) μ μ©μ μΌλΆλ‘ νμ 리μμ€(.js
, .css
λ±)λ₯Ό μμ²ν λ λ΄λΆλ§(μ¬μ€ IP, localhost)μ λν μ κ·Ό μ νμ κ²ν μ€μ΄λ€. μ μ μ μ© μΌμ μ λΆν¬λͺ
νλ, 미리 λ³΄κΈ°λ‘ μΌλΆ κΈ°λ₯μ 체νν μ μλ€.
κ³΅κ° μΉ μ¬μ΄νΈ(κ³΅κ° IPλ‘ μ κ·Ό κ°λ₯ν μ¬μ΄νΈ)μμ λ΄λΆλ§μ νμ 리μμ€(.js
, .css
λ±) μμ² μ λ°λμ 보μ 컨ν
μ€νΈ(HTTPS)λ₯Ό μ¬μ©νλλ‘ μ ννλ€. λ΄λΆλ§μ νμ 리μμ€μ μ κ·Όνλ €λ©΄ 보μ 컨ν
μ€νΈλ₯Ό μ¬μ©νλλ‘ λ³κ²½ν΄μΌ νλ€.
Chrome 86 μ΄μμ chrome://flags
μμ ν΄λΉ κΈ°λ₯μ νμ±νν΄ μ κ·Ό μ νμ 미리 체νν μ μλ€.
λ΄λΆλ§μ νμ 리μμ€ μμ² μ μ¬μ μμ²μ λ³΄λ΄ μ°κ²° νμ© μ¬λΆλ₯Ό λ¨Όμ 묻λλ€.
Chrome 98 μ΄μμ chrome://flags
μμ ν΄λΉ κΈ°λ₯μ νμ±νν΄ μ¬μ μμ² λ°μ‘μ 미리 체νν μ μλ€.