整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          利用js和css,頁面如何實現(xiàn)禁止復(fù)制與禁止調(diào)試等限制操作

          我們開發(fā)系統(tǒng)的時候,可能會接到這樣的需求:不要讓用戶復(fù)制頁面上的文字或者圖片,不要讓用戶調(diào)試我們的頁面,更甚至也不要讓用戶進(jìn)行打印操作等等。

          聽起來是不是讓人很頭大,這咋實現(xiàn)啊?這有必要嗎?這能禁住么?

          如果你沒做過這些,或者沒接到過這樣的需求,那你也應(yīng)該看到過某個網(wǎng)站做了一些這樣的措施。

          既然要做,我們就得想方案,先來看看禁止復(fù)制都有哪些方法。

          禁止復(fù)制

          假設(shè)我們有這樣一段代碼:

          <div style="padding-left: 56px;">
            <textarea rows="5" cols="33"></textarea>
          </div>
          <pre>
            海客談瀛洲,煙濤微茫信難求,
          
            越人語天姥,云霞明滅或可睹。
          
            天姥連天向天橫,勢拔五岳掩赤城。
          
            天臺四萬八千丈,對此欲倒東南傾。
          
            我欲因之夢吳越,一夜飛度鏡湖月。
          
            湖月照我影,送我至剡溪。
            </pre>

          接下來就通過這個例子來論述我們的方案:

          x效果

          1. 通過user-select:none

          這是一個css屬性,標(biāo)識了元素及其子元素的文本不可被選中,因此設(shè)定之后,文本將不能夠被選中,因此也就不能復(fù)制:

          <pre style="user-select: none;">
            海客談瀛洲,煙濤微茫信難求,
          
            越人語天姥,云霞明滅或可睹。
          
            天姥連天向天橫,勢拔五岳掩赤城。
          
            天臺四萬八千丈,對此欲倒東南傾。
          
            我欲因之夢吳越,一夜飛度鏡湖月。
          
            湖月照我影,送我至剡溪。
            </pre>

          我們在這段文本上,加上這個樣式。

          效果

          可以看到,文字壓根就不能選擇,從鼠標(biāo)形狀也能看出來。

          1. 通過攔截copy操作

          由于用在進(jìn)行復(fù)制操作的時候,會觸發(fā)copy事件,我們可以通過監(jiān)聽它來做一些處理,使得復(fù)制的行為發(fā)生改變:

          <div>
            <div style="padding-left: 56px;">
              <textarea rows="5" cols="33"></textarea>
            </div>
            <pre id="content">
              海客談瀛洲,煙濤微茫信難求,
          
              越人語天姥,云霞明滅或可睹。
          
              天姥連天向天橫,勢拔五岳掩赤城。
          
              天臺四萬八千丈,對此欲倒東南傾。
          
              我欲因之夢吳越,一夜飛度鏡湖月。
          
              湖月照我影,送我至剡溪。
            </pre>
          </div>
          <script>
            let c = document.getElementById('content')
            c.removeEventListener("copy", copyFilter)
            c.addEventListener("copy", copyFilter)
            function copyFilter(e) {
              let cp = e.clipboardData || window.clipboardData
              if(!cp) {
                return
              }
              let text = window.getSelection().toString()
              if(text) {
                e.preventDefault()
                cp.setData("text/plain", "你復(fù)制了一段魔法")
              }
            }
          </script>

          先獲取到我們要禁止復(fù)制的元素,然后給它添加一個copy的事件監(jiān)聽,在添加監(jiān)聽之前,要先移除一下,這樣是為了避免局部刷新的時候重復(fù)添加,然后我們通過copyFilter函數(shù)來對這次操作進(jìn)行處理。

          先獲取剪貼板對象,如果當(dāng)前事件對象里不存在,那就從window里面取,然后我們通過getSelection再拿到選取的內(nèi)容,因為我們對剪貼板對象進(jìn)行修改,所以要阻止默認(rèn)行為,然后把剪貼板的內(nèi)容重新賦值,可以是示例中那樣的一段文字,也可以設(shè)置為空,甚至是任意其他內(nèi)容,然后我們就可以看到產(chǎn)生的效果了:

          效果

          雖然能復(fù)制文本,但是由于我們攔截了復(fù)制操作,更改了它的行為,因此再粘貼的時候就變成了我們更改的樣子,也做到了禁止復(fù)制的功能。

          這種方式對于使用快捷鍵或者右鍵的方式都是有效的。

          1. 通過攔截cut操作

          這種情況主要是在可編輯區(qū)域,比如文本框、文本域、設(shè)置為contenteditable的元素等,用戶可以對文字進(jìn)行剪切操作,雖然上面禁止了復(fù)制,但是剪切是另一個操作,不攔截的話還是相當(dāng)于能復(fù)制出來。

          copy和cut只是觸發(fā)的事件不同而已,但是它們都是執(zhí)行相同的邏輯處理:

          <div>
            <div style="padding-left: 56px;">
              <textarea rows="5" cols="33"></textarea>
            </div>
            <pre id="content" contenteditable>
              海客談瀛洲,煙濤微茫信難求,
          
              越人語天姥,云霞明滅或可睹。
          
              天姥連天向天橫,勢拔五岳掩赤城。
          
              天臺四萬八千丈,對此欲倒東南傾。
          
              我欲因之夢吳越,一夜飛度鏡湖月。
          
              湖月照我影,送我至剡溪。
            </pre>
          </div>
          <script>
            let c = document.getElementById('content')
            c.removeEventListener("cut", copyFilter)
            c.addEventListener("cut", copyFilter)
            function copyFilter(e) {
              let cp = e.clipboardData || window.clipboardData
              if(!cp) {
                return
              }
              let text = window.getSelection().toString()
              if(text) {
                e.preventDefault()
                cp.setData("text/plain", "你復(fù)制了一段魔法")
              }
            }
          </script>

          這里我為了方便,給元素添加了contenteditable屬性,讓它變成可編輯的,copyFilter函數(shù)沒有變化,我們只是添加了一個剪切事件的監(jiān)聽,然后它們的處理函數(shù)都是copyFilter。看下效果:

          效果

          可以看到,首先我們對文字進(jìn)行剪切,沒有出現(xiàn)預(yù)期的效果,這時因為我們在代碼里面對剪切進(jìn)行了攔截,并阻止了它的默認(rèn)行為,然后我們在粘貼的時候,文字也改變成我們設(shè)置的了。

          1. 通過媒體查詢控制打印

          雖然我們可以通過上面的幾種方法禁止在頁面上復(fù)制,但是用戶也可能開啟打印預(yù)覽模式,在這種情況下,也是可以進(jìn)行復(fù)制的,我們要想對打印頁面進(jìn)行一些控制,那么就要用到媒體查詢,先看下打印的樣子:

          效果

          雖然我們做了限制,但是在打印頁面沒有生效,現(xiàn)在我們針對這個場景更改一下代碼:

          @media print {
            html {
              display: none;
            }
          }

          通過添加上面這個樣式規(guī)則,我們能夠使頁面在打印的時候,內(nèi)容隱藏起來,這樣就無法進(jìn)行復(fù)制了:

          效果

          能夠看到,點(diǎn)擊打印的時候,預(yù)覽頁面一片空白,,這樣就禁止了在打印頁面進(jìn)行復(fù)制的操作。當(dāng)然了,你其實也可以設(shè)置其他的樣式屬性來做些控制,但要記住寫在打印的媒體查詢里面,只有這樣才會在打印頁面生效。

          1. 通過偽元素覆蓋內(nèi)容

          還有一種方式就是,通過設(shè)定一個偽元素,讓它全面覆蓋文本內(nèi)容,這樣鼠標(biāo)就不能選到實際的文本,改造一下代碼:

          .content {
            position: relative;
          }
          .content::before {
            content: '';
            position: absolute;
            left: 0px;
            top: 0px;
            width: 100%;
            height: 100%;
          }
          <pre id="content" class="content">
            海客談瀛洲,煙濤微茫信難求,
          
            越人語天姥,云霞明滅或可睹。
          
            天姥連天向天橫,勢拔五岳掩赤城。
          
            天臺四萬八千丈,對此欲倒東南傾。
          
            我欲因之夢吳越,一夜飛度鏡湖月。
          
            湖月照我影,送我至剡溪。
          </pre>

          現(xiàn)在就不能在元素上面選中文字了,不過用戶也可能還有一些操作,比如在內(nèi)容區(qū)域外面ctrl+a全選,或者在外面拖動鼠標(biāo)來全選,如果是這種情形,那么我們可以通過監(jiān)聽鍵盤和鼠標(biāo)事件來禁止全選等操作。

          1. 通過監(jiān)聽鼠標(biāo)和鍵盤事件

          由于用戶有很多種操作的方式,鍵盤全選、鼠標(biāo)全選、鍵盤右鍵、鼠標(biāo)右鍵等等,我們?nèi)绻F舉的話,情況太多了,因此我們只監(jiān)聽鼠標(biāo)按下和抬起事件,以及鍵盤的按下事件:

          document.removeEventListener("mousedown", haveSelect)
          document.addEventListener("mousedown", haveSelect)
          document.removeEventListener("mouseup", haveSelect)
          document.addEventListener("mouseup", haveSelect)
          document.removeEventListener("keydown", haveSelect)
          document.addEventListener("keydown", haveSelect)
          function haveSelect() {
            window.getSelection().removeAllRanges()
          }

          主要就是在removeAllRanges方法上面,能夠在觸發(fā)上面事件的時候,將所選區(qū)域清空,也就是不管你選沒選,咋選的,反正就是你只要進(jìn)行了操作,那我就那可能選擇的區(qū)域給你清空,這樣你就啥也干不了了。

          效果

          好,這樣就可以啦,無論怎么選,即使出現(xiàn)了選區(qū),但是只要你再按了鼠標(biāo)或者鍵盤,那么選區(qū)就會直接消失,就能達(dá)到不能復(fù)制的效果,因為你發(fā)現(xiàn)啥都做不了。

          這里額外說明一下,對于事件的監(jiān)聽,一定要用addEventListener來實現(xiàn),因為它會將多個綁定的事件都添加上去,當(dāng)觸發(fā)的時候就會按照綁定的順序進(jìn)行執(zhí)行,如果是用賦值的方式,那么后面的會覆蓋前面的,而且賦值的方式很容易被篡改,可以很輕松的讓你的綁定函數(shù)不能執(zhí)行從而失效,而使用addEventListener就不會被人為覆蓋,只能通過綁定的函數(shù)句柄來手動移除,也就是說要移除的時候,必須使用跟綁定時使用同一個函數(shù)才行。

          通過CSS的方式禁止復(fù)制,可以很容易的被用戶解除,只能是設(shè)置的稍微復(fù)雜一點(diǎn),增加難度。而通過JS的方式禁止復(fù)制,也可以通過禁用頁面JavaScript代碼來解除,因此我們可以將內(nèi)容通過js來渲染,這樣如果頁面禁用了js,那么內(nèi)容也不會渲染。

          禁止調(diào)試

          對于禁止調(diào)試,主要是指用戶打開控制臺,控制臺也就是開發(fā)者工具,我為了方便稱之為控制臺,想要對頁面進(jìn)行調(diào)試時,我們做一些處理,阻止這種行為,最大可能的攔截通過控制臺對系統(tǒng)的調(diào)試。
          主要的方法有幾種,由于打開控制臺是瀏覽器提供的調(diào)試功能,因此我們沒法攔截打開操作,即使通過事件監(jiān)聽不允許快捷鍵這樣做,但是也可以通過其他方式進(jìn)行打開,因此我們的主要關(guān)注點(diǎn)就在于打開控制臺之后,我們能做哪些事情來限制用戶行為。

          1. 禁用快捷鍵

          打開控制臺的快捷鍵主要有F12和ctrl+shift+i,我們先把這倆給禁用了:

          document.removeEventListener("keydown", disableDevShortcut)
          document.addEventListener("keydown", disableDevShortcut)
          function disableDevShortcut(e) {
            console.log(e)
            if(e.keyCode === 123) {
              e.preventDefault()
            }else if(e.keyCode === 73 && e.ctrlKey && e.shiftKey) {
              e.preventDefault()
            }
          }

          這樣在使用這兩個快捷鍵的時候,頁面沒有任何反應(yīng),控制臺也不會喚起,因為我們阻止了它們的默認(rèn)行為。

          1. 禁用右鍵的檢查功能

          除了通過快捷鍵,還可以使用右鍵的方式,并點(diǎn)擊檢查也會調(diào)出控制臺。

          效果

          這種情景,我們可以通過禁止在頁面上使用右鍵的方式,來阻止打開控制臺:

          document.removeEventListener("contextmenu", cancelContextmenu)
          document.addEventListener("contextmenu", cancelContextmenu)
          function cancelContextmenu(e) {
            e.preventDefault()
          }

          現(xiàn)在就不能通過右鍵打開控制臺了,但是相應(yīng)的整個右鍵功能也都不能使用了。

          1. 設(shè)置無限斷點(diǎn)

          如果用戶最終打開了控制臺,比如通過在瀏覽器的更多功能中來打開的話,那么我就需要采取其他的措施,其中之一就是給代碼設(shè)置無限斷點(diǎn),因為斷點(diǎn)只在控制臺打開的時候才會發(fā)生作用,從而不必?fù)?dān)心非調(diào)試模式下的程序正常運(yùn)行。

          無限斷點(diǎn)的主要思路就是利用定時器等手段,頻繁的觸發(fā)斷點(diǎn)效果,使得不能輕松的調(diào)試程序,先看下代碼:

          ;(() => {
            function breakDebugger() {
              if(new checkDebugger().check) {
                breakDebugger()
              }
            }
            function checkDebugger() {
              const now = new Date();
              eval('(function () {debugger;false;})()')
              const dur = Date.now() - now
              if(dur < 5) {
                return {check: false}
              }else {
                return {check: true}
              }
            }
            setInterval(() => {
              eval('(function () {debugger;true;})()')
              breakDebugger()
            }, 500)
          })()

          我們利用一個立即執(zhí)行的自執(zhí)行函數(shù),來使我們的代碼被封裝在一個固定塊內(nèi),不與其他部分有任何影響。

          這里主要做了兩步:

          第一步設(shè)置一個重復(fù)執(zhí)行的定時器,其中包括了一個斷點(diǎn)和一個函數(shù)調(diào)用。

          第二步通過函數(shù)來遞歸調(diào)用斷點(diǎn),主要使用了實例化對象的方式和時間差的判斷。

          這樣做的主要作用就是在設(shè)置無限斷點(diǎn)的同時,也能夠讓每次的斷點(diǎn)都是被重新生成的,看下效果,一目了然:

          效果

          發(fā)現(xiàn)沒有,我們通過這種方式,只要打開了控制臺,那么就會進(jìn)入到無限斷點(diǎn)的循環(huán)中,使得不能做任何其他事情,而且每個斷點(diǎn)的生成都會開辟一個新的虛擬運(yùn)行環(huán)境,這種情況下,只有關(guān)閉控制臺,才能結(jié)束斷點(diǎn)。

          即使使用右鍵選擇Never parse here,也毫無作用,雖然可以通過Deactive breakpoints按鈕來徹底禁用斷點(diǎn),就是下面這個按鈕:

          按鈕

          但是,如果這樣做的話,那么用戶也就同時失去了調(diào)試其他代碼的能力。

          況且,我們接下來還會介紹其他的控制手段,可以配合著使用。

          1. 監(jiān)測控制臺開啟

          我們?nèi)绻苡幸环N手段,可以知道用戶開啟了控制臺,換句話說只要控制臺被打開,就通知我們或者被我們監(jiān)測到,那么我們就可以執(zhí)行一些控制手段,這種效果肯定是很理想的,遺憾的是還沒有這種api暴露給我們?nèi)プ屛覀兡軌蜻@樣做。

          不過我們可以通過其他的方式,利用既有的一些能力來實現(xiàn)這一點(diǎn),這里我還是使用循環(huán)定時器,來不斷的去嗅探用戶是否開啟了控制臺,直接看代碼:

          ;(() => {
            setInterval(function() {
              let foo = document.createElement('a')
              let a1 = +new Date()
              console.table(foo)
              let a2 = +new Date()
              if(a2 - a1 > 1) {
                location.href = 'about:blank'
              }
              console.clear()
            }, 500)
          })()

          同樣,通過一個自執(zhí)行函數(shù),我們開啟了一個循環(huán)定時器,然后在回調(diào)方法里面,我們就去實現(xiàn)上面的目標(biāo),也是分為了兩步:

          第一步創(chuàng)建一個a元素,然后通過表格的形式將它打印出來,并記錄下消耗的時間。

          第二步判斷耗時的長短來控制是否跳轉(zhuǎn)到空白頁,然后清空控制臺。

          這種方式主要是利用了console.table的特性,它會將元素以表格的形式輸出到控制臺,大概就像下面的樣子:

          效果

          由于太多了,我就沒有全部截下來,如果沒有打開控制臺的話,使用console.table輸出我們創(chuàng)建的a標(biāo)簽是很快的,有多快呢,就是js執(zhí)行一條語句的速度,所以打印a1和a2的時間間隔非常短,幾乎為0,因為他們快到差不多是同時執(zhí)行的,給大家打印看一下:

          效果

          我們先不打開控制臺,等輸出完畢再打開,很清楚的發(fā)現(xiàn),我們沒打開控制臺的時候,輸出的a就是它標(biāo)簽,而且時間間隔是0毫秒。

          現(xiàn)在我們打開控制臺的時候刷新一下頁面,看看控制臺的輸出:

          效果

          這次就變成了以table的形式輸出a元素,而且它的耗時明顯增多,不再是0毫秒,而是耗費(fèi)了10毫秒,雖然打開控制臺的時候多次刷新頁面,每次輸出的毫秒數(shù)是不同的,但是跟關(guān)閉控制臺的時候輸出的耗時差距非常明顯,因此我們就可以在這個上面做文章。

          我在上面的代碼中假定了,只要是大于1毫秒的耗時,那就表示用戶打開了控制臺,然后我們就把頁面給跳轉(zhuǎn)到空白頁,當(dāng)然了你也可以做一些任何你想做的操作,比如彈出一個提示,或者把body內(nèi)容置空等等等等。

          回到我們上面的代碼,看一下它實際發(fā)生的作用和帶給我們的效果:

          效果

          哈哈,古德古德,平時瀏覽一切正常,只要剛一打開控制臺,瞬間頁面就被跳轉(zhuǎn)走了,什么都干不了。這樣我們就通過這種方式,達(dá)到了限制打開控制臺的目的,也就是在當(dāng)前頁沒法調(diào)試,一打開就跳轉(zhuǎn)。

          這種辦法由于是繞路實現(xiàn)的,那么你可能會有疑問,它穩(wěn)定嗎?會不會誤判,我可以對它絕對放心嗎?

          理論上來說,通過輸出的執(zhí)行時間是不太能精確掌握的,但是我們可以再做一些其他的措施來逼近真相:

          ;(() => {
            setInterval(function() {
              let foo = document.createElement('a')
              let a1 = +new Date()
              console.table(foo)
              let a2 = +new Date()
              if(a2 - a1 > 1) {
                let time = 0
                for(let i = 0; i < 10; i++) {
                  let a1 = +new Date()
                  console.table(foo)
                  let a2 = +new Date()
                  time += a2 - a1
                }
                if(time > 20) {
                  location.href = 'about:blank'
                }
              }
              console.clear()
            }, 500)
          })()

          我又改造了一下判斷的邏輯,當(dāng)發(fā)現(xiàn)輸出耗時為2毫秒甚至更多的時候,我立馬再進(jìn)行一次真?zhèn)闻袛啵簿褪钦f,萬一由于其他的影響,導(dǎo)致我第7行的代碼誤判了,那么我再同步執(zhí)行一個循環(huán),連續(xù)輸出10次,把他們的耗時總和計算出來,然后判斷是否大于20毫秒,如果還是耗時過高的話,那么就可以非常肯定的知道用戶是打開了控制臺,這個時候就可以放心的做一些處理了。

          最后

          其實限制用戶行為的方法有很多很多,上面列出了一些主要的,多種方法還是要結(jié)合著使用。你也可以自由發(fā)揮,多使用一些其他的手段,也會增加用戶復(fù)制或者調(diào)試的難度,比如防止用戶重寫console的方法,或者清除所有定時器等。

          甚至也可以將你的內(nèi)容繪制到canvas上面來防止復(fù)制,多加一些js的處理工作,防止禁用js的時候,我們的代碼不生效,只有在js可用的時候再去渲染內(nèi)容等。也可以在綜合考慮的情況下加上代碼混淆、代碼加密等措施。

          話說回來,大家都是同路人,何必相互為難,哈哈哈,不過提這個需求的人也著實會為難我們,既然提了那就盡力去做,能做到什么程度,只能說是盡量做到極致。

          希望上面的內(nèi)容能夠幫助到你,也希望能夠?qū)δ阌兴鶈l(fā)。

          謝謝

          這篇文章中,描述并演示了一些有趣的 HTML 屬性,您可能聽說過也可能沒有聽說過,并且可能會發(fā)現(xiàn)它們非常有用,可以在您的項目中親自使用。

          有次和公司大佬閑余時間進(jìn)行討論時,說到了一個問題:

          今年你很高興學(xué)習(xí)或更深入地學(xué)習(xí)哪些語言/技術(shù)?

          我的:typescript、next.js、react、graphql、solidity、node


          他的答案很簡單:HTML。而且我一點(diǎn)也沒有諷刺或嘲弄。當(dāng)然,我非常清楚在哪些情況下使用哪些標(biāo)簽,以及如何使我的 HTML 大部分具有語義性和可訪問性。

          但是我確信我已經(jīng)忘記了一大堆較少使用的屬性,并且可能有一大堆我甚至不知道存在的屬性。這篇文章是我學(xué)習(xí)的結(jié)果,我希望你會發(fā)現(xiàn)其中的一些對你有用,因為大部分初學(xué)者在接下來的幾個月里正在構(gòu)建 HTML 頁面。

          enterkeyhint虛擬鍵盤 的屬性

          iOS 13.7+,以及Chrome77+新支持了一個HTML屬性enterkeyhint,可以修改軟鍵盤中的enter鍵,也就是回車鍵的文案或者UI。

          該HTML屬性的兼容性如下圖所示:

          enterkeyhint接受七個可能值之一,這些值將確定用戶在他的“輸入”鍵上看到的內(nèi)容:

          enter

          表示回車,多出現(xiàn)在<textarea>文本域等需要多行輸入的場景中。

          go

          表示前往,, 意思是把用戶帶到他們輸入的文本的目標(biāo)處。

          next

          表示下一項,通常會移動到下一個輸入項那里。

          previous

          ‘previous’表示上一個,通常會移動到上一個輸入項那里。

          search

          ‘search’表示搜索,通常用在搜索行為中。

          send

          ‘send’表示發(fā),通常用在文本信息的發(fā)送上。

          done

          ‘done’表示完成,表示沒有更多內(nèi)容輸入,輸入結(jié)束。

          只是強(qiáng)調(diào)一下,這個屬性不接受自定義值;該值需要是上面顯示的七個之一。無法識別的值將默認(rèn)為輸入鍵的設(shè)備默認(rèn)文本。

          <link>標(biāo)簽的title屬性

          這對我來說是全新的,可能是此列表中最有趣的一個。作為一些背景知識,如果您不知道,F(xiàn)irefox 有一個選項可讓您選擇查看頁面時要使用的樣式表。通常,此功能顯示兩個選項:“基本頁面樣式”和“無樣式”,如下圖所示。

          這使您可以快速測試禁用樣式時頁面的外觀,還允許您使用任何備用樣式表查看頁面。

          備用樣式表功能通過兩個屬性啟用:title屬性和rel=alternate應(yīng)用于<link>元素,如下面的代碼所示:

          <link href="main.css" rel="stylesheet" title="Default">
          <link href="contrast.css" rel="alternate stylesheet" title="High Contrast">
          <link href="readable.css" rel="alternate stylesheet" title="Readable">

          在這種情況下,我的“默認(rèn)”樣式將自動應(yīng)用,但僅當(dāng)我使用 Firefox 的“頁面樣式”選項選擇它們時,備用樣式表才會應(yīng)用。

          <blockquote>標(biāo)簽定義及用法

          <blockquote>標(biāo)簽定義塊引用。

          <blockquote>與</blockquote>之間的所有文本都會從常規(guī)文本中分離出來,經(jīng)常會在左、右兩邊進(jìn)行縮進(jìn)(增加外邊距),而且有時會使用斜體。也就是說,塊引用擁有它們自己的空間。

          <blockquote></blockquote>這個標(biāo)簽可以將其包起來的文字,全部往右縮排。而且加一組標(biāo)簽,往右縮排一單位,加兩組標(biāo)簽,往右縮排兩單位,依此類推。

          現(xiàn)在我們來看看長文本的引用,還有和p標(biāo)簽的對比,效果如圖:

          自定義有序列表的屬性<ol>

          <ol>經(jīng)常使用使用該元素的有序列表。一些鮮為人知的功能允許您自定義出現(xiàn)在此類列表中的編號行為:

          • 屬性,以相反的順序?qū)?/span>reversed項目進(jìn)行編號(從高到低,而不是默認(rèn)的從低到高);
          • 屬性,定義從start哪個數(shù)字開始;
          • 屬性,定義是type使用數(shù)字、字母還是羅馬數(shù)字;
          • 屬性,用于在value特定列表項上指定自定義編號。

          如您所見,使用純 HTML 的有序列表比您通常習(xí)慣的要靈活得多。

          reversed屬性是一個有趣的屬性,因為它實際上并沒有反轉(zhuǎn)列表本身的內(nèi)容;它只會反轉(zhuǎn)每個列表項旁邊的數(shù)字。

          <ol reversed>
              <li>List item...</li>
              <li>List item...</li>
              <li>List item...</li>
          </ol>

          結(jié)果是這樣的排序

          上面,我還提到了其他三個屬性。讓我們將它們合并到列表中,看看如何使用它們:

          <ol reversed start="20" type="1">
              <li>Typee: A Peep at Polynesian Life (1846)</li>
              <li>Omoo: A Narrative of Adventures in the South Seas (1847)</li>
              <li>Mardi: and a Voyage Thither (1849)</li>
              <li>Redburn: His First Voyage (1849)</li>
              <li value="100">White-Jacket; or, The World in a Man-of-War (1850)</li>
              <li>Moby-Dick; or, The Whale (1851)</li>
              <li>Pierre; or, The Ambiguities (1852)</li>
              <li>Isle of the Cross (1853 unpublished, and now lost)</li>
          </ol>

          請注意,已添加的type和屬性以及單個列表項上的屬性。該屬性接受表示編號類型的五個單字符值(、、、、 )之一效果如圖:

          結(jié)果展示

          <a>標(biāo)簽的download屬性

          <a href="/example.pdf" download>Download File</a>

          如果沒有值,該download屬性會強(qiáng)制下載鏈接頁面。或者,您可以提供一個值,瀏覽器將其用作下載資源的建議文件名。

          <img>標(biāo)簽的decoding屬性

          在研究這個標(biāo)簽時,這對我來說是另一篇全新的文章——而且在規(guī)范中似乎相當(dāng)新。將decoding屬性添加到圖像元素可為瀏覽器提供圖像解碼提示。

          <img src="/images/example.png" alt="Example" decoding="async">

          此屬性類似于async在腳本上使用該屬性。加載圖像所需的時間不會改變,但其“解碼”的方式(因此其內(nèi)容在視口中變得可見)由decoding屬性決定。

          值為:

          • sync 同步解碼圖像,一般瀏覽器都是這樣做的。
          • async 異步解碼圖像以避免延遲其他內(nèi)容的呈現(xiàn)。
          • auto 默認(rèn)允許瀏覽器使用自己的內(nèi)置解碼方法。

          <iframe>標(biāo)簽 的loading屬性

          您可能已經(jīng)知道,圖像元素現(xiàn)在可以包含一個loading屬性,該屬性將延遲加載作為一項功能放入瀏覽器,這是我們多年來使用 JavaScript 解決方案所做的事情。但不要忘記該loading屬性也可以用于<iframe>元素:

          <iframe src="/page.html" width="300" height="250" loading="lazy">

          與圖像一樣,該loading屬性接受eager(默認(rèn)瀏覽器行為)或 的值lazy,這會延遲 iframe 內(nèi)容的加載,直到 iframe 即將進(jìn)入視口。此屬性的唯一缺點(diǎn)是 Firefox 不支持在 iframe 上使用它(盡管 Firefox 確實支持loading圖像)。

          刪除/插入的citeAnddatetime屬性

          我在處理塊引用時已經(jīng)提到cite過,但是這個屬性也可以用于用<del>and<ins>元素標(biāo)記的刪除和插入。此外,兩個元素都可以包含一個datetime屬性。

          <del
            cite="https://bugzilla.mozilla.org/show_bug.cgi?id=1620467"
            datetime="2020-07-23"
          >Firefox doesn't support CSS's standard <code>appearance</code> property, so you can only use it prefixed.</del>
          
          <ins          
            cite="https://bugzilla.mozilla.org/show_bug.cgi?id=1620467"
            datetime="2020-07-23"
          >The <code>appearance</code> property, previously only available prefixed in Firefox, can now be used in all modern browers unprefixed.</ins>

          對于每個元素,這兩個屬性代表的內(nèi)容如下:

          • cite 指向資源的 URL,該資源解釋了刪除或插入內(nèi)容的原因。
          • datetime 刪除或插入的日期。

          HTML <optgroup> 標(biāo)簽

          通過 <optgroup> 標(biāo)簽把相關(guān)的選項組合在一起:

          <select>
            <optgroup label="Swedish Cars">
              <option value ="volvo">Volvo</option>
              <option value ="saab">Saab</option>
            </optgroup>
          
            <optgroup label="German Cars">
              <option value ="mercedes">Mercedes</option>
              <option value ="audi">Audi</option>
            </optgroup>
          </select>

          結(jié)果展示

          optgroup 元素用于組合選項。當(dāng)您使用一個長的選項列表時,對相關(guān)的選項進(jìn)行組合會使處理更加容易。

          用于預(yù)加載響應(yīng)式圖像的imagesizes和imagesrcset屬性

          這是我在研究本文時的另一對新屬性,它們在規(guī)范中也是相對較新的。

          這兩個屬性都可以與元素一起定義rel=preloadas如下<link>所示:

          <link rel="preload"
            as="image"
            imagesrcset="images/example-480.png 480w,
            images/example-800.png 800w,
              images/example.png 2000w"
            imagesizes="(max-width: 600px) 480px,
            (max-width: 1000px) 800px,
              1000px"
            src="images/example.png"
            alt="Example Image">

          這里的使用rel=preload通知瀏覽器我們希望指定的資源優(yōu)先加載,因此它們不會被腳本和樣式表之類的東西阻塞。該as屬性指定所請求內(nèi)容的類型。

          您可以使用href屬性以及preload和預(yù)加載常規(guī)圖像as。但最重要的是,您可以使用imagesrcsetandimagesizes屬性,就像我在上面的代碼中所做的那樣。

          這允許您預(yù)加載正確的圖像,具體取決于視口的大小或您在imagesizes屬性中指定的其他媒體功能。

          除了我已經(jīng)詳細(xì)描述和演示的屬性之外,您可能還想了解其他一些屬性,我將在這里簡要提及:

          • crossorigin可以應(yīng)用于多個元素的屬性,包括<audio><img><link><script><video>,為跨域資源共享(CORS)提供支持;
          • HTML屬性;<dfn><abbr>
          • HTML的新disablepictureinpicture屬性;<video>
          • HTMLintegrity屬性,幫助瀏覽器驗證資源沒有被不當(dāng)操作;
          • HTMLdisabled屬性<fieldset>,輕松同時禁用多個表單元素;
          • 電子郵件和文件輸入的multiple屬性。

          如果您使用過本文中提到的任何屬性,或者如果您知道在您的項目中使用過的另一個 HTML 屬性,請隨時在評論中告訴我。

          TML標(biāo)簽規(guī)范

          盡管目前瀏覽器都兼容HTML,但是,使網(wǎng)頁能夠符合標(biāo)準(zhǔn),應(yīng)該盡量使用XHTML規(guī)范來編寫代碼,需要注意以下事項:

          (1)在XHTML中,標(biāo)簽名必須小寫。在HTML中,標(biāo)簽名稱可以大寫,也可以小寫。

          (2)在XHTML中,屬性名稱必須小寫。在HTML中,屬性名稱也必須是小寫的。

          (3)在XHTML中,標(biāo)簽必須嚴(yán)格嵌套;HTML對標(biāo)簽的嵌套沒有嚴(yán)格的規(guī)定。

          (4)在XHTML中,標(biāo)簽必須封閉;在HTML規(guī)范中,標(biāo)簽不閉合也是正確的,即標(biāo)簽可以不成對出現(xiàn)。例如,“<p>我沒有結(jié)束標(biāo)簽”和“<p>我有開始標(biāo)簽和結(jié)束標(biāo)簽</p >”,這兩條代碼的顯示效果,在HTML規(guī)范中是完全相同的,但是,在XHTML中,第一條語句是不允許的(不能正常顯示),必須像第二條語句那樣,嚴(yán)格地使標(biāo)簽閉合。

          (5)在XHTML中,即使是空元素的標(biāo)簽也必須封閉,這里說的空標(biāo)簽,就是指那些<img>、<br>、<hr>等不對稱的標(biāo)簽,它們也必須閉合,即寫作<img>、<br>、<hr/>;而在HTML.規(guī)范中,這類標(biāo)簽可以不封閉。例如,在HTML中書寫為<img>或<img/>均是正確的,但在XHTML規(guī)范中,必須寫為<img/>才正確。

          (6)在XHTML中,屬性值必須用雙引號括起來;在HTML中,屬性可以不必使用雙引號。

          (7)在XHTML中,屬性值必須使用完整形式;在HTML中,一些屬性經(jīng)常使用簡寫方式設(shè)定屬性值。例如:

          <input disabled>

          而在XHTML中,必須完整地寫做:

          <input disabled="true">

          (8)在XHTML中,應(yīng)該區(qū)分“內(nèi)容標(biāo)簽”與“結(jié)構(gòu)標(biāo)簽”。例如,<p>標(biāo)簽是一個內(nèi)容標(biāo)簽,而<table>標(biāo)簽是結(jié)構(gòu)標(biāo)簽,因此,不允許將<table>標(biāo)簽置于<p>標(biāo)簽內(nèi)部。反之,如果將<p>標(biāo)簽置于<td>…</td>之間,則是完全正確的。

          (9)在XHTML中,必須添加文檔類型聲明<!DOCTYPE>。該標(biāo)簽用于描述HTML的版本和文檔類型聲明,進(jìn)行了相應(yīng)的版本和文檔類型聲明之后,才能保證這是一個XHTML網(wǎng)頁,從而讓瀏覽器以相應(yīng)的規(guī)范來解析網(wǎng)頁,使網(wǎng)頁正常顯示。


          上一篇:HTML 鏈接
          下一篇:HTML 編輯器
          主站蜘蛛池模板: 中文人妻av高清一区二区| 日韩一区在线视频| 亚洲无线码在线一区观看| 熟女少妇精品一区二区| 亚洲一区在线视频| 欧洲精品一区二区三区| 亚洲V无码一区二区三区四区观看| 激情无码亚洲一区二区三区| 亚洲国产精品无码第一区二区三区| 深夜福利一区二区| 亚洲国产成人久久综合一区77| 无码人妻AⅤ一区二区三区| 久久精品岛国av一区二区无码| 免费看无码自慰一区二区| 国产一区在线mmai| 精品人伦一区二区三区潘金莲| 一区二区三区视频观看| 国产精品熟女视频一区二区 | 波多野结衣一区二区免费视频 | 精品三级AV无码一区| 亚洲免费视频一区二区三区| 91久久精品无码一区二区毛片| 精品无码中出一区二区| 久久精品亚洲一区二区三区浴池| 韩国一区二区三区视频| 国产亚洲无线码一区二区| 精品无码综合一区二区三区| 黑人大战亚洲人精品一区| 精品国产免费观看一区| 精品动漫一区二区无遮挡| 久久免费国产精品一区二区| 国产一区二区精品久久岳| 精品乱码一区二区三区在线| 日韩精品无码Av一区二区| 天天视频一区二区三区| 日本一区二区三区在线视频观看免费 | 精品国产一区二区三区久| 亚洲国产欧美日韩精品一区二区三区 | 久久久久久人妻一区精品| 丰满人妻一区二区三区免费视频 | 中文字幕av无码一区二区三区电影|