雖然CSS簡(jiǎn)單,但CSS是一門(mén)非常有意思的語(yǔ)言,,CSS每年都有變化,,而且都有不同的博主都在不同的時(shí)間段總結(jié)一些CSS的新特性,。雖然這些新特性無(wú)法立刻得到眾多瀏覽器的支持,,但總是隨著時(shí)間的發(fā)展,這些特性都會(huì)得到瀏覽器的支持,。哪怕未得到支持,,也有一些方法讓瀏覽器支持,比如最為出外的cssnext,,就可以讓很多未來(lái)的CSS特性就立馬使用,,并且不用花太多時(shí)間來(lái)考慮瀏覽器的兼容性。 接下來(lái)要介紹的五個(gè)CSS新特性是: 這些CSS特性,,估計(jì)有些同學(xué)已經(jīng)接觸過(guò)了,,如果你未接觸過(guò),建議你繼續(xù)跟隨著下面的步驟繼續(xù)往下閱讀,。 案例:創(chuàng)建一個(gè)新聞提要(Newsfeed)通過(guò)一個(gè)新聞提要為例,,分不同的步驟向大家闡述這個(gè)新聞提要是怎么制作的,以及在制作這個(gè)案例的時(shí)候,,這五個(gè)CSS特性是如何在案例中得到運(yùn)用,。 Step1:新聞提要的HTML模板我們這個(gè)案例其實(shí)很簡(jiǎn)單,并未使用任何JavaScript框架,,還是使用原始的HTML結(jié)構(gòu)來(lái)做這個(gè)Demo,。所以我們需要一些簡(jiǎn)單的HTML的標(biāo)簽,幫助我們創(chuàng)建Demo,。這里使用了一個(gè)類(lèi)名為.container 的div ,,該div 包含了一個(gè)類(lèi)名為.feed 的ul ,然后創(chuàng)建了十個(gè)li ,,每個(gè)li 包含了一個(gè)類(lèi)名為.card 的div ,。 在第五個(gè)和第六個(gè)li 之間創(chuàng)建了另一個(gè)名為nested 的li ,其包含了一個(gè)無(wú)序列表ul ,而且包含了三個(gè)li 創(chuàng)建三個(gè)卡片,。 <div class="container">
<ul class="feed">
<li><div class="card">Card 1</div></li>
<li><div class="card">Card 2</div></li>
<li><div class="card">Card 3</div></li>
<li><div class="card">Card 4</div></li>
<li><div class="card">Card 5</div></li>
<li class="nested">
<ul>
<li><div class="card">Card A</div></li>
<li><div class="card">Card B</div></li>
<li><div class="card">Card C</div></li>
</ul>
</li>
<li><div class="card">Card 6</div></li>
<li><div class="card">Card 7</div></li>
<li><div class="card">Card 8</div></li>
<li><div class="card">Card 9</div></li>
<li><div class="card">Card 10</div></li>
</ul>
</div>
在沒(méi)有任何樣式的情況之下,,你看到的效果是這樣的: <iframe id="MBvPvX" src="https:///airen/embed/MBvPvX?height=400&theme-id=0&slug-hash=MBvPvX&default-tab=result&user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="box-sizing: inherit; width: 4860px; overflow: hidden;"></iframe> Step2:添加樣式現(xiàn)在要給示例添加一些基本樣式,,使其看起來(lái)更像一個(gè)新聞提要: body {
background-color: grey;
}
.container {
max-width: 800px;
margin: 0 auto;
}
.card {
background-color: #fff;
padding: 10px;
margin: 10px;
min-height: 300px;
}
最后,,在.feed 上使用Flexbox相關(guān)的特性,讓每行有兩張卡片: .feed {
display: flex;
flex-wrap: wrap;
li {
flex: 1 0 50%;
}
}
效果如下: <iframe id="mjMzGO" src="https:///airen/embed/mjMzGO?height=400&theme-id=0&slug-hash=mjMzGO&default-tab=result&user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="box-sizing: inherit; width: 4860px; overflow: hidden;"></iframe> 如果你從未接觸過(guò)Flexbox相關(guān)的知識(shí),,強(qiáng)烈建議你花點(diǎn)時(shí)間閱讀這些文章,。因?yàn)镕lexbox發(fā)展到今天,已經(jīng)開(kāi)始取代float 來(lái)布局,,成為最主流的布局方式之一,,特別是在移動(dòng)端上的布局。
Step03:解決布局問(wèn)題當(dāng)你向下滾動(dòng)列表時(shí),,你會(huì)發(fā)現(xiàn).nested 下的三個(gè)li (對(duì)應(yīng)的是CardA ~ CardC )影響了整體的布局效果: image 其實(shí)我們想要的,,或者說(shuō)理想狀態(tài)下,所有的卡片按流的方式排列,,但事實(shí)并未如此,。造成這種現(xiàn)象的原因是Flex容器 —— ul.feed 設(shè)置了display:flex (創(chuàng)建了一個(gè)Flex容器),創(chuàng)建Flex容器之后,,只會(huì)對(duì)其子元素(ul.feed > li.card )有影響,,即可子元素自動(dòng)會(huì)變成Flex項(xiàng)目。但不會(huì)影響其后代子元素,,換句話(huà)說(shuō),,.nested > li 是無(wú)法自動(dòng)變成Flex項(xiàng)目,。 通常解決這個(gè)問(wèn)題的唯一方法是更改HTML模板,但有些情況之下,,比如說(shuō)在CMS系統(tǒng)中(假設(shè)你沒(méi)有修改HTML標(biāo)簽的權(quán)利),,那么面對(duì)這種情況,你就會(huì)束手無(wú)策了,。當(dāng)然,,你也許會(huì)想到使用JavaScript來(lái)處理?;蛟S以前你會(huì)這么想,,但時(shí)至今日,咱們可以通過(guò)新的CSS特性來(lái)解決這個(gè)問(wèn)題 —— display:contents ,。 W3C規(guī)范是這樣對(duì)display:contents 描述的: “The element itself does not generate any boxes, but its children and pseudo-elements still generate boxes as normal. For the purposes of box generation and layout, the element must be treated as if it had been replaced with its children and pseudo-elements in the document tree.“
大至意思是:“元素本身不產(chǎn)生任何邊界框,,而元素的子元素與偽元素仍然生成邊界框,元素文字照常顯示,。為了同時(shí)照顧邊界框與布局,,處理這個(gè)元素時(shí),要想象這個(gè)元素不在元素樹(shù)型結(jié)構(gòu)里,,而只有內(nèi)容留下,。這包括元素在原文檔中的子元素與偽元素,比如::before 和::after 這兩個(gè)偽元素,,如平常一樣,,前者仍然在元素子元素之前生成,后者在之后生成,。” 那么display: contents 這一簡(jiǎn)單的代碼實(shí)際上讓元素表現(xiàn)得好像不存在一樣,。但仍然可以看到元素的后代,而且元素自身并不影響布局,。也就是說(shuō),,.nested 的子元素.card 也將變成Flex項(xiàng)目。 首先刪除現(xiàn)有.feed li 的類(lèi)名,,然后在ul 和li 是使用display: contents : .feed ul,
.feed li {
display: contents;
}
這個(gè)時(shí)候.feed 下所有的.card 都變成了Flex項(xiàng)目(不僅是.feed 下的子元素li ,,還包括后代的li 元素): image 現(xiàn)在你看到的所有卡片都是有序的排列,但是尺寸不對(duì): image 可以通過(guò)在.card 上添加flex 屬性來(lái)解決這個(gè)問(wèn)題: .card {
flex: 1 0 40%;
}
這個(gè)時(shí)候每張卡片的尺寸就又恢復(fù)正常了: <iframe id="KBvbZg" src="https:///airen/embed/KBvbZg?height=400&theme-id=0&slug-hash=KBvbZg&default-tab=result&user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="box-sizing: inherit; width: 4860px; overflow: hidden;"></iframe> 這個(gè)時(shí)候就好象ul 不存在了一樣,。如果你夠仔細(xì)的話(huà),,你可以發(fā)現(xiàn)flex-basis 的值設(shè)置為40% 了,雖然我們?cè)O(shè)置了所有元素的box-sizing 的值為border-box ,,但大家都知道,,box-sizing 可以影響盒模型的計(jì)算,但對(duì)margin 不包括在內(nèi),,所以為了有足夠的空間放置卡片,,把flex-basis 的值重新計(jì)算了,,也就是大家所看到的40% 。 這個(gè)示例也再次向大家說(shuō)明了display:contents 的神奇之處,。當(dāng)然,,這里并沒(méi)有對(duì)display:contents 做詳細(xì)的介紹,但也足夠向大家展示其強(qiáng)大之處,。如果你對(duì)該特性感興趣,,或者想深入的學(xué)習(xí),建議閱讀下面這幾篇文章: Step04:探索CSS查詢(xún)特性盡管display:contents 實(shí)現(xiàn)了我們想要的效果,,但它仍然處于W3C的工作草案狀態(tài),。目前只在Chrome 65+、Firefox 59+ 中看到效果,。 如果你在瀏覽器開(kāi)發(fā)者工具中,,禁掉display: contents ,你可以看到你的布局又開(kāi)始混亂了,。這樣做只是模擬瀏覽器不支持該屬性時(shí)的效果,。那么我們接下來(lái)能做什么呢?這就引出了下一個(gè)CSS新特性 —— CSS查詢(xún)特性,。 它的原理有點(diǎn)類(lèi)似于CSS中的媒體查詢(xún)(@media )一樣,,但是它允許你單獨(dú)使用CSS表達(dá)式,類(lèi)似于JavaScript語(yǔ)言中的if / else 之類(lèi),。如果條件符合應(yīng)用對(duì)應(yīng)塊中的樣式,。接下來(lái)讓我們把display:contents 作為查詢(xún)特性的條件,然后將對(duì)應(yīng)的CSS樣式放置在{...} 塊中,。就像下面這樣: @supports (display: contents) {
.feed ul,
.feed li {
display: contents;
}
.card {
flex: 1 0 40%;
}
}
<iframe id="QBMYRx" src="https:///airen/embed/QBMYRx?height=400&theme-id=0&slug-hash=QBMYRx&default-tab=result&user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="box-sizing: inherit; width: 4860px; overflow: hidden;"></iframe> 在CSS中,,查詢(xún)特性很多時(shí)候也被稱(chēng)為CSS的條件特性,,其主要包括@media ,、@supports 和@viewport 。有關(guān)于這方面的介紹可以閱讀@webinista寫(xiě)的PPT —— 《Conditional CSS》,。
可能你第一次接觸到@supports() 的話(huà)會(huì)感到很好奇,,并不知道該屬性的具體使用,如果你愿意的話(huà),,建議你花點(diǎn)時(shí)間閱讀早期整理過(guò)的文章《CSS3條件判斷:@supports 》和《說(shuō)說(shuō)CSS中的@supports 》,。 Step05: 使用not 關(guān)鍵讓代碼變得更清晰在CSS的世界中,像@supports 這樣其實(shí)也就是一種漸進(jìn)增強(qiáng)和優(yōu)雅降級(jí)的方案,。我們可以使用@supports 來(lái)添加新的樣式,,但也可以添加降級(jí)所需的一些原始樣式。 如果忽略IE瀏覽器的話(huà),,@supports 已得到很好的支持,。實(shí)際上你可能希望使用的是CSS查詢(xún)特性,,而不是某一種操作符。它的工作方式和你預(yù)期的一樣,,因此我們可以通過(guò)@supports 的not 關(guān)鍵詞對(duì)那些不支持display: contents 瀏覽器添加對(duì)應(yīng)的樣式,。基于這個(gè)原因,,我們可以把示例的代碼修改成: // 支持 display: contents的瀏覽器,,采用的是這段代碼
@supports (display: contents) {
.feed ul,
.feed li {
display: contents;
}
.card {
flex: 1 0 40%;
}
}
// 不支持display:contents的瀏覽器,采用下面這段代碼
@supports not (display: contents) {
.feed li {
flex: 1 0 50%;
}
.feed li.nested {
flex-basis: 100%;
}
.feed li.nested ul {
display: flex;
flex-wrap: wrap;
}
}
<iframe id="XBaGZm" src="https:///airen/embed/XBaGZm?height=400&theme-id=0&slug-hash=XBaGZm&default-tab=result&user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="box-sizing: inherit; width: 4860px; overflow: hidden;"></iframe> 在支持display:contens 的瀏覽器,,你將看到的效果如下: image 在不支持display:contents 的瀏覽器,,看到的效果又像下面這樣: image Step06: 更進(jìn)一步優(yōu)化經(jīng)過(guò)上面的示例,估計(jì)你已經(jīng)體會(huì)到了CSS查詢(xún)特性的魅力與潛力了,,上面用到的僅僅是查查詢(xún)特性中的部分功能,,更強(qiáng)大的是你可以and 、or 和not 結(jié)合起來(lái),,讓你的條件表達(dá)式更為強(qiáng)大,。比如說(shuō),你的降級(jí)方案除了考慮display:contents 之外,,還會(huì)說(shuō)有可能用戶(hù)的瀏覽器對(duì)display:flex 也不支持,。在這樣的情況之下,咱們可以繼續(xù)降級(jí)到float 的布局,。 不過(guò)我們?cè)谶@里不會(huì)考慮降級(jí)到float 的布局,。但我們可以對(duì)display: flex 和display:contents 進(jìn)行降級(jí)處理。這里會(huì)用到@supports 中的and 和not 關(guān)鍵詞,。上面的代碼就變成像下面這樣: @supports (display: flex) and (display: contents) {
.feed ul,
.feed li {
display: contents;
}
.card {
flex: 1 0 40%;
}
}
@supports (display: flex) and (not (display: contents)) {
.feed li {
flex: 1 0 50%;
}
.feed li.nested {
flex-basis: 100%;
}
.feed li.nested ul {
display: flex;
flex-wrap: wrap;
}
}
<iframe id="JByVbM" src="https:///airen/embed/JByVbM?height=400&theme-id=0&slug-hash=JByVbM&default-tab=result&user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="box-sizing: inherit; width: 4860px; overflow: hidden;"></iframe> 甚至你還可以在@supports 中使用CSS的自定義屬性,,比如像下面這樣: @supports (--foo: green) {
...
}
如果你對(duì)@supports 或CSS查詢(xún)特性相關(guān)的知識(shí)點(diǎn)還不足夠滿(mǎn)足的話(huà),建議你閱讀下面的文章,,深入的學(xué)習(xí)這方面的知識(shí): 案例:聊天框現(xiàn)在我們有了一個(gè)漂亮的新聞提要(Newsfeed),,接下來(lái)在前面的Newsfeed基礎(chǔ)上添加一個(gè)小的聊天框,這個(gè)聊天框固定在屏幕的右下角,。 Step7: 添加聊天框我們需要一個(gè)消息列表和一個(gè)文本域字段,,方便用戶(hù)輸入消息。那么在<body> 標(biāo)簽的后面添加這個(gè)聊天框所需要的HTML標(biāo)簽: <div class="chat">
<div class="messages">
<ul>
<li><div class="message">Message 1</div></li>
<li><div class="message">Message 2</div></li>
<li><div class="message">Message 3</div></li>
<li><div class="message">Message 4</div></li>
<li><div class="message">Message 5</div></li>
<li><div class="message">Message 6</div></li>
<li><div class="message">Message 7</div></li>
<li><div class="message">Message 8</div></li>
<li><div class="message">Message 9</div></li>
<li><div class="message">Message 10</div></li>
</ul>
</div>
<input type="text" class="input">
</div>
在沒(méi)有給聊天添加任何樣式的情況下,,我們看到的效果是: <iframe id="pZrBKR" src="https:///airen/embed/pZrBKR?height=400&theme-id=0&slug-hash=pZrBKR&default-tab=result&user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="box-sizing: inherit; width: 4860px; overflow: hidden;"></iframe> Step08:給聊天框添加樣式先給聊天框添加一些基本樣式,,讓它看起來(lái)有點(diǎn)像聊天框的樣子: .chat {
background: #fff;
border: 10px solid #000;
bottom: 0;
font-size: 10px;
position: fixed;
right: 0;
width: 300px;
}
.messages {
border-bottom: 5px solid #000;
overflow: auto;
padding: 10px;
max-height: 300px;
}
.message {
background: #000;
border-radius: 5px;
color: #fff;
margin: 0 20% 10px 0;
padding: 10px;
}
.messages li:last-child .message {
margin-bottom: 0;
}
.input {
border: none;
display: block;
padding: 10px;
width: 100%;
}
效果看起來(lái)像下面這樣: <iframe id="yqorwX" src="https:///airen/embed/yqorwX?height=400&theme-id=0&slug-hash=yqorwX&default-tab=result&user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="box-sizing: inherit; width: 4860px; overflow: hidden;"></iframe> Step09:滾動(dòng)鏈接現(xiàn)在頁(yè)面上可以看到已經(jīng)美化好的聊天框了,這個(gè)聊天框有一個(gè)可滾動(dòng)的消息列表和一個(gè)文本輸入框,,而且位于前面創(chuàng)建子的Newsfeed上面(如果沒(méi)有的話(huà),,你可以把你的瀏覽器縮小),如下: image 看上去是不是不錯(cuò),。但是你有沒(méi)有注意到,,當(dāng)你滾動(dòng)聊天框中的信息列表到底部的時(shí)候,會(huì)發(fā)生什么,?感興趣的話(huà),,親自試一試。咱們做兩個(gè)小測(cè)試,,先滾動(dòng)頁(yè)面body ,,看看效果: image 然后再聊天框的信息列表中滾動(dòng),一直滾動(dòng)到最底端,,滾不動(dòng)為止,,看看效果以是: image 滾動(dòng)Newsfeed,和我們想象的并沒(méi)有差異,;但滾動(dòng)聊天框中的消息列表時(shí),,卻不一樣,滾動(dòng)到消息列表末端時(shí),,可以看到頁(yè)面body 將開(kāi)始滾動(dòng),。這種效果被稱(chēng)為滾動(dòng)鏈接,即Scroll Chaining,。 在我們這個(gè)示例中,,這可能不是什么大問(wèn)題,但在某些情況下,,它可能就是一大問(wèn)題了,。比如Modal彈框,那就很有必要解決這樣現(xiàn)象,。 比較拙的解決方案就是給body 添加overflow:hidden ,,但這有可能會(huì)影響我們的操作,甚至影響你瀏覽你的頁(yè)面,。但值得慶幸的是,,CSS有一個(gè)新特性可以做得更為完美,體驗(yàn)更佳,,而且使用起來(lái)并不復(fù)雜,,只需要一行代碼即可,,那就是CSS的overscroll-behavior ,,這個(gè)屬性有三個(gè)可取值: auto :其默認(rèn)值。元素(容器)的滾動(dòng)會(huì)傳播給其祖先元素,。有點(diǎn)類(lèi)似JavaScript中的冒泡行為一樣
contain :阻止?jié)L動(dòng)鏈接,。滾動(dòng)行為不會(huì)傳播給其祖先元素,但會(huì)影響節(jié)點(diǎn)內(nèi)的局部顯示。例如,,Android上的光輝效果或iOS上的回彈效果,。當(dāng)用戶(hù)觸摸滾動(dòng)邊界時(shí)會(huì)通知用戶(hù)。注意,,overscroll-behavior:contain 在html 元素上使用,,可以阻止導(dǎo)航滾動(dòng)操作
none :和contain 一樣,但它也可以防止節(jié)點(diǎn)本身的滾動(dòng)效果
overscroll-behavior 屬性是overscroll-behavior-x 和overscroll-behavior-y 的簡(jiǎn)寫(xiě),,如果你只想控制其中一個(gè)方向的滾動(dòng)行為,,可以使用其中的某一個(gè)屬性。
回到我們的示例來(lái),,在.messages 類(lèi)中添加下面這行代碼: .messages {
overscroll-behavior-y: contain;
}
<iframe id="xJLNWo" src="https:///airen/embed/xJLNWo?height=400&theme-id=0&slug-hash=xJLNWo&default-tab=result&user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="box-sizing: inherit; width: 4860px; overflow: hidden;"></iframe> 現(xiàn)在你再?lài)L試一下,,在聊天框中的消息列表中上下滾動(dòng)。此時(shí)你再滾動(dòng)到消息列表末端時(shí),,它不再會(huì)影響body 的滾動(dòng)了(頁(yè)面的滾動(dòng)): image 如果你想在PWA中實(shí)現(xiàn)下拉刷新的效果,,比如下拉時(shí)刷新Newsfeed,那么這個(gè)屬性就非常方便,。只需要在body 或html 元素中添加overscroll-behavior:contain 即可,。 值得注意的是,這個(gè)屬性還不是W3C標(biāo)準(zhǔn),,而是Web孵化器WICG的一個(gè)建議,。不過(guò),說(shuō)不定哪一天,,這個(gè)特性就進(jìn)入到W3C工作組中,,成為W3C的一個(gè)標(biāo)準(zhǔn)。 有關(guān)于這方面的更多介紹,,建議閱讀下面幾篇文章: Step10:折疊聊天框目前,,聊天框占據(jù)了相當(dāng)大的空間,如果我們不與其交互的話(huà),,會(huì)有點(diǎn)分散用戶(hù)的注意力,。辛運(yùn)的是,我們可以用CSS的選擇器特性來(lái)解決這個(gè)問(wèn)題,。這也是CSS的另一新特性,,再一次向大家展示了CSS的魔力。 首先調(diào)整一下現(xiàn)有的樣式,。默認(rèn)情況下,,我們希望聊天框是處理一個(gè)折疊狀態(tài),因此把.message 的max-height 值重置一下,,在此設(shè)置為0 ,,并且把padding 也重置為0 ,。因?yàn)檫@個(gè)值剛剛好折疊了聊天框,而且又不影響其美觀(guān),。為了讓聊天框折疊和展開(kāi)時(shí)有一個(gè)過(guò)渡的動(dòng)畫(huà)效果,,借助CSS的transition 屬性來(lái)實(shí)現(xiàn)。 .messages {
...
max-height: 0;
padding: 0;
transition: max-height 500ms;
}
效果看起來(lái)還不錯(cuò),,如下所示: <iframe id="Owjeyz" src="https:///airen/embed/Owjeyz?height=400&theme-id=0&slug-hash=Owjeyz&default-tab=result&user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="box-sizing: inherit; width: 4860px; overflow: hidden;"></iframe> Step11:當(dāng)聊天框得到焦點(diǎn)時(shí),,展開(kāi)聊天框現(xiàn)在我們的聊天框中的信息列表是看不到。因?yàn)槲覀兦懊姘研畔⒘斜碚郫B起來(lái)了?,F(xiàn)在我們要思考的是如何通過(guò)CSS來(lái)將其展開(kāi),。這就會(huì)用到CSS的另一新特性 —— :focus-within 。 有點(diǎn)類(lèi)似于:focus 偽類(lèi)選擇器一樣,,但是:focus-within 與其不同之處是,,如果元素的任何后代元素得到焦點(diǎn),它就會(huì)被匹配,。這就是這個(gè)屬性特別之處,,因?yàn)樗cCSS通常的工作方式相反,通常我們只能根據(jù)元素的祖先來(lái)選擇元素,。 在我們這個(gè)示例中,,當(dāng).chat 區(qū)域內(nèi)的任何內(nèi)容得到焦點(diǎn)時(shí),重置一下.message 的max-height 和padding 值,。請(qǐng)記住,,一個(gè)元素必須接受鍵盤(pán)或鼠標(biāo)事件或其他形式的輸入,以便接收焦點(diǎn),。比如我們這個(gè)示例,,點(diǎn)擊<input> 輸入框就符合這個(gè)要求,可以達(dá)到我們想要的預(yù)期效果,。 .chat:focus-within .messages {
max-height: 300px;
padding: 10px;
}
<iframe id="xJLoXO" src="https:///airen/embed/xJLoXO?height=400&theme-id=0&slug-hash=xJLoXO&default-tab=result&user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="box-sizing: inherit; width: 4860px; overflow: hidden;"></iframe> 你現(xiàn)在可以嘗試一下效果,。點(diǎn)擊input 讓其得到焦點(diǎn),可以看到聊天框可以展開(kāi),,反之聊天框又會(huì)折疊起來(lái): image Step12:進(jìn)一步突顯:focus-within 的魔力如果僅僅實(shí)現(xiàn)聊天框的折疊和展開(kāi)效果,,到上一步其實(shí)已經(jīng)完成了。但對(duì)于一位有追求的前端,,總是在嘗試很多極限性,。回到我們的示例中來(lái),,如果PM跟你提了一個(gè)新需求,,當(dāng)文本輸入框得到焦點(diǎn)之后,除了能展開(kāi)聊天框之外,,還希望聊天框底下的Newsfeed變得模糊,。對(duì)于這樣的一個(gè)效果,,怎么來(lái)實(shí)現(xiàn)呢,? 要實(shí)現(xiàn)這樣的效果,,其實(shí)并不復(fù)雜,如果你有做過(guò)自定義單選按鈕或復(fù)選框(當(dāng)然是純CSS),,你應(yīng)該會(huì)想到解決方案,。我們可以使用CSS選擇器中的兄弟組合器~ ,就可以很容易的做到這一點(diǎn),。使用~ 選擇器有一個(gè)前提需要注意,,聊天框.chat 需要在Newsfeed(.container )前面(指的是HTML結(jié)構(gòu),事實(shí)上我們已經(jīng)這樣做了),。只有這樣才能通過(guò)下面的方式讓Newsfeed變得模糊: .chat:focus-within ~ .container {
filter: blur(5px)
}
<iframe id="RBLojW" src="https:///airen/embed/RBLojW?height=400&theme-id=0&slug-hash=RBLojW&default-tab=result&user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="box-sizing: inherit; width: 4860px; overflow: hidden;"></iframe> 當(dāng)然,,這可能不是最佳的一個(gè)方案,但僅通過(guò)CSS的技術(shù)手段就能達(dá)到預(yù)期的效果,,已經(jīng)很酷了,。感興趣的話(huà),自己可以體驗(yàn)一下: image 注意,,Newfeed添加了filter 效果,,這將會(huì)改變?cè)氐膶盈B順序,造成聊天框在Newsfeed下面,。所以需要顯式的.chat 中添加z-index 的值,。比如這里設(shè)置了z-index: 1001 。具體原因可以查閱@張?chǎng)涡窭蠋煹摹?a href="https://www./wordpress/?p=5115" target="_blank" style="box-sizing: border-box; color: rgb(49, 148, 208); background-color: transparent; outline: none; transition: color 0.3s; touch-action: manipulation;">深入理解CSS中的層疊上下文和層疊順序》一文,。
探索:placeholder-shown image 首先要先分清楚,,:placeholder-shown 和::placeholder 是不同的兩個(gè)東東。神奇的是:placholder-shown 是W3C標(biāo)準(zhǔn)規(guī)范的一個(gè)屬性,,而::placeholder 卻不是,。::placeholder-shown 仍然會(huì)影響占位符文本的樣式。 注意::placeholder-shown 是一個(gè)偽類(lèi)選擇器(它是一個(gè)處于特定狀態(tài)的元素),;::placeholder 是一個(gè)偽元素(一個(gè)在DOM中并不存在的可見(jiàn)元素),。
另外,:placeholder-shown 也是新的選擇器之一(CSS Selectors Module Level 4新增了很多種偽類(lèi)選擇器),,它可以匹配任何顯示占位符文本的輸入,。在我們的示例中,文本輸入框(input )并沒(méi)有任何占位符文本,,所以先在HTML中的input 元素中,,添加placeholder ,新增占位符文本,。 <input type="text" class="input" placeholder="Enter your message">
然后在input 之后添加一個(gè)新的元素,,用來(lái)幫助用戶(hù)操作: <div class="prompt">Press enter to send</div>
現(xiàn)在給這個(gè)幫助信息.prompt 添加一些樣式,,默認(rèn)情況之它是被折疊起來(lái)了。 .prompt {
line-height: 2em;
max-height: 0;
overflow: hidden;
padding: 0 10px;
text-align: right;
transition: max-height 500ms;
}
<iframe id="ZjXKjM" src="https:///airen/embed/ZjXKjM?height=400&theme-id=0&slug-hash=ZjXKjM&default-tab=result&user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="box-sizing: inherit; width: 4860px; overflow: hidden;"></iframe> 僅從外觀(guān)上看,,似乎好像沒(méi)多出什么,,就是在文本框中多了一個(gè)占位符文本: image 雖然沒(méi)多大區(qū)別,但這為后續(xù)的效果已埋下了一個(gè)伏筆,。接著往下看,。 Step14:使用提示信息可見(jiàn)此時(shí)提示信息處于折疊狀態(tài),并不可見(jiàn),,我想大家也想到了,,要怎么使用:placeholder-shown 讓其可見(jiàn)?大多數(shù)瀏覽器會(huì)顯示占位符文本,,直到用戶(hù)在input 中輸入真的值,。為了提高用戶(hù)使用表單的體驗(yàn),如果input 得到對(duì)應(yīng)的焦點(diǎn)之后,,占位符文本并不隱藏,,還起著提示作用,是不是更有意思,,也對(duì)用戶(hù)有更好的幫助,,畢竟我們不希望用戶(hù)發(fā)送空的消息,所以我們可以將這種行為關(guān)聯(lián)起來(lái),,只有在用戶(hù)輸入值時(shí)才顯示提示信息(也就是.prompt 展開(kāi)可見(jiàn)),。 :placeholder-shown 表示的是占位文本符可見(jiàn)的狀態(tài),而提示信息可見(jiàn)的時(shí)候,,占位文本符不可見(jiàn),,也就是input 有了一個(gè)真正的值。換句話(huà)來(lái)說(shuō),,我們需要有一個(gè):placeholder-shown 的反轉(zhuǎn)(占位文本符不可見(jiàn)),,這個(gè)時(shí)候我們可以借助:not() 選擇器來(lái)幫我們做這樣的反轉(zhuǎn)。
.input:not(:placeholder-shown) + .prompt {
max-height: 2em;
}
將max-height 設(shè)置為font-size:10px 的兩倍,,這里使用了2em ,,這個(gè)時(shí)候可以展開(kāi)提信信息塊。簡(jiǎn)單而有整潔,。如果這個(gè)看似平凡的偽類(lèi)選擇器能過(guò)通過(guò)最終的規(guī)范,,那么我們將會(huì)看到一些巧妙的運(yùn)用。來(lái)到這一步,,效果變成: <iframe id="jpGwmQ" src="https:///airen/embed/jpGwmQ?height=400&theme-id=0&slug-hash=jpGwmQ&default-tab=result&user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="box-sizing: inherit; width: 4860px; overflow: hidden;"></iframe> 親自體驗(yàn)一下,,你在input 隨便輸入一點(diǎn)內(nèi)容,哪怕是空格,,也能看到提示信息被展示出來(lái)了: image 不管:focus-within 還是:placeholder-shown ,,它們都是CSS選擇器新增加的偽類(lèi)選擇器,,如果感興趣,建議你花些時(shí)間對(duì)這些方面進(jìn)行了一了解: Step15:讓它充滿(mǎn)生機(jī)到目前為止,,我們通過(guò)簡(jiǎn)單的HTML和一些CSS特性完成了一個(gè)帶有聊天功能的新聞提要的基本架構(gòu),,但是目前它是沒(méi)有生命的,只是一個(gè)純靜態(tài)的東西,。也就是說(shuō)用戶(hù)并不有用它做任何事情,。這個(gè)案例包含了一些有趣的CSS新特性,,但到現(xiàn)在為止不能修改DOM,。如果想讓這個(gè)案例更為生動(dòng),那么就需要借助一些JavaScript功能,,以便用戶(hù)能通過(guò)聊天框添加消息,。 首先,需要向<input> 和類(lèi)名.messages 的子元素ul 添加一個(gè)ID ,,以便JavaScript更好的獲取到對(duì)應(yīng)的元素,。同時(shí)給input 元素添加一個(gè)required 屬性,當(dāng)用戶(hù)未輸入任何信息的時(shí)候,,表單可以自動(dòng)較驗(yàn),。 <ul id="messages" ...
<input type="text" id="input" required ...
然后創(chuàng)建一個(gè)名為script.js 文件,并且放置在</body> 之前,。不過(guò)我們的案例是在Codepen上做相應(yīng)的演示,,所以無(wú)需考慮創(chuàng)建一個(gè)單的.js 文件。 Step16:添加一些JavaScript我們需要給<input> 添加一個(gè)事件函數(shù),,當(dāng)監(jiān)聽(tīng)到鍵盤(pán)的Enter 事件,,獲取到input 的值(如果有效)并將其添加到消息列表的末尾,清除字段并滾動(dòng)到消息的底部,。 // 獲取相應(yīng)的元素
const input = document.getElementById('input');
const messages = document.getElementById('messages');
// 監(jiān)聽(tīng)input的鍵盤(pán)事件
input.addEventListener('keypress', (event) => {
// 檢查是否按下Enter鍵
if (event.keyCode === 13) {
// 檢查字段是否有效
if (input.validity.valid) {
// 使用該值創(chuàng)建DOM元素
const message = createMessage(input.value);
// 將新創(chuàng)建的DOM元素添加到消息列表
messages.appendChild(message);
// 清除輸入框的值
input.value = '';
// 滾動(dòng)到消息列表的底部
messages.parentNode.scrollTop = messages.parentNode.scrollHeight;
}
}
});
// 將input的值轉(zhuǎn)換為HTML的字符串
function createMessage (value) {
return stringToDom(`<li><div class="message">${value}</div></li>`)
}
// 將字符串轉(zhuǎn)換為真實(shí)的DOM
function stringToDom (string) {
const template = document.createElement('template');
template.innerHTML = string.trim();
return template.content.firstChild;
}
<iframe id="VBMBWP" src="https:///airen/embed/VBMBWP?height=400&theme-id=0&slug-hash=VBMBWP&default-tab=result&user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="box-sizing: inherit; width: 4860px; overflow: hidden;"></iframe> 現(xiàn)在,,當(dāng)你在input 中輸入字段并按Enter 鍵時(shí),你將看到你輸入的消息添加到消息列表的底部,。 image Step17:添加一引起額外的信息為了向大家演示最后一個(gè)CSS新特性 —— contain,,咱們需要做一些設(shè)計(jì)。我們將實(shí)現(xiàn)一個(gè)效果,,在消息列表頂部的框中發(fā)送新消息的時(shí)間,。當(dāng)你將鼠標(biāo)懸停在消息上時(shí),就會(huì)有這個(gè)效果,。 首先,,我們需要將這些信息添加到我們的新消息中。我們可以修改createMessage 函數(shù)返回的值,。 function createMessage (value) {
return stringToDom(`
<li>
<div class="message message--mine" data-timestamp="${new Date().toString()}"> ${value} </div>
</li>
`);
}
你已經(jīng)注意到了,,在message 中新增加了一個(gè)類(lèi)message--mine ,。并給這個(gè)類(lèi)添加相應(yīng)的樣式: .message--mine {
background: #ff2089;
margin-left: 20%;
margin-right: 0;
}
<iframe id="OwxogG" src="https:///airen/embed/OwxogG?height=400&theme-id=0&slug-hash=OwxogG&default-tab=result&user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="box-sizing: inherit; width: 4860px; overflow: hidden;"></iframe> 當(dāng)你新輸入內(nèi)容,按下Enter 鍵時(shí),,新增加的消息列表對(duì)應(yīng)的結(jié)構(gòu)就變成像下圖這樣的: image 顯示時(shí)間戳我們的目的是將創(chuàng)建消息的時(shí)間戳顯示在消息列表的頂部,,我們需要這樣做,以便即使在滾動(dòng)消息列表時(shí),,這個(gè)時(shí)間戳也總是可見(jiàn)的,。這里我們借助CSS的偽元素來(lái)做: .message--mine::after {
content: attr(data-timestamp);
}
這個(gè)時(shí)候你所看到的效果是這樣的: image 這個(gè)效果并不是我們想要的。我們?cè)跇邮缴仙宰鲂薷?,鼠?biāo)懸浮到新添加的消息列表上時(shí)才能看到時(shí)間戳,,而且這個(gè)時(shí)間戳固定在消息區(qū)域的頂部。 .message--mine:hover::after {
background: #000;
color: #ff2089;
content: attr(data-timestamp);
left: 0;
padding: 5px;
position: fixed;
top: 0;
width: 100%;
}
這個(gè)效果現(xiàn)在變成這樣了: image 現(xiàn)在時(shí)間戳固定在頁(yè)面的頂部,,可以繼續(xù)優(yōu)化一下,,在.messages 中添加position: relative 。 但也不起作用,,那是因?yàn)楣潭ǘㄎ皇窍鄬?duì)于viewport 的,,而不是相對(duì)于其祖先元素。那么這個(gè)時(shí)候,,最后一個(gè)CSS新特性應(yīng)該要出場(chǎng)了,。 <iframe id="pZWOKb" src="https:///airen/embed/pZWOKb?height=400&theme-id=0&slug-hash=pZWOKb&default-tab=result&user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="box-sizing: inherit; width: 4860px; overflow: hidden;"></iframe> Step19:探索ContainmentCSS Containment是一個(gè)令人興奮的新命題。它有許多選項(xiàng),,可以限制瀏覽器的樣式,、布局和對(duì)特定元素的繪制。這在修改DOM時(shí)特別有用,。在瀏覽器中,,哪怕是很小的變化都有可能造成瀏覽器重繪整個(gè)頁(yè)面,這樣的消費(fèi)是很貴,,即使瀏覽器努力為我們做了很多優(yōu)化,,頁(yè)面的重繪還是對(duì)性能有一定影響的。 使用CSS Containment,,我們可以把頁(yè)面的部分圈起來(lái),,然后說(shuō)“這里發(fā)生了什么,只在這里做相應(yīng)的事”,。這也是另一種方法,,可以保護(hù)元素不受外部的變化而受影響。 image CSS的Containment是一個(gè)新屬性,,使用關(guān)鍵字contain ,,它支持四個(gè)屬性值: layout :這個(gè)值打開(kāi)該元素的布局控制。這確保所包含元素對(duì)布局目的完全不透明;外部不能影響其內(nèi)部布局,,反之亦然
paint :這個(gè)值打開(kāi)該元素的繪制控制,。這確保包含元素的后代節(jié)點(diǎn)不顯示在其邊界外,因此,,如果一個(gè)元素在屏幕外或是不可見(jiàn)的,,它的后代節(jié)點(diǎn)同樣也被保證是不可見(jiàn)的
size :這個(gè)值打開(kāi)該元素的尺寸控制。這確保包含元素可以無(wú)需檢查其后代節(jié)點(diǎn)進(jìn)行布局,。
style :這個(gè)值打開(kāi)該元素的樣式控制,。這確保了,對(duì)于性能這會(huì)不僅僅作用于一個(gè)元素及其后代,,這些效果也不忽視包含的元素
您還可以結(jié)合關(guān)鍵字,,如contain: layout paint ,這將僅適用于一個(gè)元素的這些行為,。但也包含支持兩個(gè)額外的值: 每個(gè)值都有點(diǎn)不透明,,所以我建議你閱讀規(guī)范并在開(kāi)發(fā)者工具中使用它們來(lái)查看實(shí)際發(fā)生的情況,。 layout 和paint 是兩個(gè)重要的值,,因?yàn)樗鼈冊(cè)谛枰罅坎僮鱀OM時(shí),對(duì)性能有一定的優(yōu)化,。然而,,在我們的演示中,我們可以利用contain: paint 我助我們進(jìn)行時(shí)間戳定位,。
根據(jù)規(guī)范所描述,,當(dāng)使用paint 時(shí),“元素作為一個(gè)包含絕對(duì)定位和固定定位后代的塊”,。這意味著,,我們可以在.chat 上設(shè)置contain: paint ,這樣一來(lái),,.chat 中元素的固定定位將基于card 而不是viewport ,。你可以通過(guò)使用transform: translateZ(0) 獲得相同的效果。 <iframe id="KBXGXX" src="https:///airen/embed/KBXGXX?height=400&theme-id=0&slug-hash=KBXGXX&default-tab=result&user=airen" scrolling="no" frameborder="0" height="400" allowtransparency="true" allowfullscreen="true" class="cp_embed_iframe undefined" style="box-sizing: inherit; width: 4860px; overflow: hidden;"></iframe> 嘗試一下下,,效果完美了: image CSS Containment是較新的特性,,目前只在Chrome 52+版本可以看到。 前端學(xué)習(xí)圈
|