玩轉 HTML5 Placeholder

Placeholder(佔位符) 是 HTML5 新增的一個 HTML 屬性,用來對可輸入字段的指望值提供提示信息,目前已經獲得主流瀏覽器的普遍支持,使用方式很是簡單:javascript

<input id="username" name="username" placeholder="請輸入用戶名" type="text">

該屬性適用於 <textarea> 多行文本框和 type 屬性值爲 text, password, search, tel, url 或者 email 等的 <input>css

placeholder

自定義樣式

若是想改變 placeholder 的默認呈現樣式,應該使用 ::placeholder 僞元素選擇器,不過當前尚未瀏覽器支持,所以只能根據不一樣瀏覽器的不一樣實現方式分別定義:html

::-webkit-input-placeholder { /* Chrome/Safari/Opera */
  color: green;
}

::-moz-placeholder { /* Firefox 19+ */
  color: green;
}

:-ms-input-placeholder { /* IE 10+ 注意這裏只有一個冒號 */
  color: green;
}

爲何樣式要分別定義呢,若是像下面這樣組合到一塊兒:java

::-webkit-input-placeholder,
::-moz-placeholder {
  color: green;
}

不該該把針對不一樣瀏覽器的選擇器寫在一塊兒,這樣寫會由於沒法識別的選擇器而形成這整個規則集被忽略(固然,像相似 IE 7 這種具備超強錯誤處理能力的瀏覽器除外,不過這裏和 IE 7 沒什麼關係)。jquery

Firefox 定義方式的改變

若是須要兼容老版本的 Firefox 瀏覽器,還須要添加下面這種只有一個冒號的樣式規則:git

:-moz-placeholder { /* Firefox 4 - 18 */
  color: green;
}

由於從 Firefox 19 開始一個冒號的僞類定義方式 :-moz-placeholder 被廢棄了,切換爲兩個冒號的僞元素定義方式。與此同時,它還添加了一個默認的 opacity: 0.54 不透明度樣式,若是須要,能夠覆蓋掉該樣式,不然文字是半透明的:github

::-moz-placeholder {
  color: green;
  opacity: 1;
}

僞類和僞元素

僞類和僞元素有什麼區別呢?僞類能夠理解爲給某個元素添加了一個類,好比 :first-child 僞類,選擇第一個子元素:web

p:first-child {
  font-size: 16px;
}

也能夠理解爲元素當前的狀態,一樣也能夠經過添加一個真正的 class 來實現相似效果:瀏覽器

p.first-child {
  font-size: 16px;
}

不管是僞類仍是真正的類,樣式都是直接添加到 <p> 元素上的。服務器

而僞元素能夠理解爲添加了一個虛擬的元素。好比 p:before 僞元素,能夠像下面這個僞代碼這樣理解:

<before>p:before</before>
<p>paragraph</p>

這裏 <p> 元素和 p:before 能夠理解爲是兩個不一樣的元素。若是被繞暈了,不要緊,畢竟這不是本文的重點,更多僞元素與僞類的信息能夠參考 Pseudo-classes - CSS | MDNPseudo-elements - CSS | MDN

關於僞類選擇器引起的問題

由於 IE 瀏覽器使用的是僞類 :-ms-input-placeholder 選擇器來定義 placeholder 的樣式,實際上樣式是做用於文本輸入框的,若是另外還有針對文本輸入框的選擇器特殊性更高的樣式規則,將會覆蓋掉該樣式,參考下面代碼:

input:-ms-input-placeholder { /* 0, 0, 1, 1 */
  color: green;
}

#username { /* 0, 1, 0, 0 */
  color: blue;
}

註釋中的數字用來表示該選擇器的特殊性。

上面兩個樣式規則當中使用 ID 選擇器的特殊性更高,因此在 IE 10/11 中測試會看到 placeholder 顯示爲藍色,與指望的有點不同。一樣使用僞類選擇器的舊版本 Firefox 也會出現這個問題,所以,在書寫樣式的時候須要特別注意,實在搞不定,別忘了還有 !important 規則能夠用。其它使用兩個冒號的僞元素選擇器的瀏覽器不會出現這個問題,例如:

input::-webkit-input-placeholder { /* 0, 0, 0, 2 */
  color: green;
}

#username { /* 0, 1, 0, 0 */
  color: blue;
}

上面兩個樣式規則互相之間不會影響,使用 Chrome 測試 placeholder 的顏色爲綠色。

關於選擇器的特殊性能夠參考拙做CSS選擇器特殊性與重要性

讓行爲保持一致

雖然樣式是能夠自定義了,不過在行爲上還有些差別,在 Chrome 和 Firefox 中當文本輸入框輸入內容時 placeholder 纔會消失,清除內容時又會顯示出來;而在 IE 中則是當文本輸入框獲取焦點時 placeholder 就消失了,失去焦點同時沒有輸入內容時纔會從新顯示。若是但願在 Chrome 和 Firefox 等瀏覽器中實現獲取焦點就消失的效果,能夠藉助 :focus 僞類選擇器來將 placeholder 的文本顏色設置爲透明:

:focus::-webkit-input-placeholder {
  color: transparent;
}

當文本輸入框獲取焦點時,placeholder 的顏色被設置爲透明,感官上就好像消失同樣。

JavaScript

獲取或者修改 placeholder 的內容直接獲取或者修改對應文本輸入框的 placeholder 屬性的值就好了:

$('input').attr('placeholder', 'Please enter your name');

So easy,媽媽不再用擔憂我寫代碼了。不過,想要像普通 DOM 元素那樣經過 javaScript 獲取僞元素對象直接操做估計很難,目前可使用 window.getComputedStyle() 方法來獲得其樣式屬性,該方法的第二個參數是一個僞元素:

window.getComputedStyle(document.getElementById('username'),
  '::-moz-placeholder').getPropertyValue('color'); // "rgb(0, 255, 0)"

若是要經過 JavaScript 來修改 placeholder 僞元素的樣式的話比較好的一種方式是預先定義好幾種不一樣的樣式:

.style-1::-moz-placeholder {
  color: green;
}

.style-2::-moz-placeholder {
  color: red;
}

而後經過切換文本輸入框的 class 屬性來實現修改樣式的目的:

$('input').addClass('style-2').removeClass('style-1');

另外也能夠經過直接添加樣式規則來實現。

Polyfill

對於不支持該屬性的瀏覽器,會簡單地忽略掉。原則上,placeholder 僅僅是用來對指望的輸入起個提示的做用,對於不支持的瀏覽器在可用性上不受任何影響。若是須要兼容,那麼應該使用特性檢測的方式,針對不支持的瀏覽器使用 Polyfill,對於支持的瀏覽器來講,原生的固然是最好。

判斷瀏覽器是否支持 <input> 元素的 placeholder 屬性,能夠引入 Modernizr 庫來判斷:

if (!Modernizr.input.placeholder) {
  // 作點什麼事
}

也能夠本身寫判斷,簡單易行的辦法就是生成一個 <input> 元素對象,並判斷該元素對象是否具備 placeholder 屬性:

'placeholder' in document.createElement('input')

同理,對於 <textarea> 元素也是同樣:

'placeholder' in document.createElement('textarea')

另外,Opera Mini 明明不支持 placeholder 屬性,卻裝成本身很懂的樣子。這時候可使用客戶端檢測技術來將之排除掉,Opera Mini 的 window 對象包含一個 operamini 對象:

({}).toString.call(window.operamini) === '[object OperaMini]'

結合起來就是這樣:

if (!('placeholder' in document.createElement('input'))
  || ({}).toString.call(window.operamini) === '[object OperaMini]') {
  // 作點什麼事
}

在編寫 Polyfill 的時候應該儘可能與原生功能保持一致,我這裏選擇向 IE 的方式看齊,即當文本輸入框獲取或失去焦點的時候處理 placeholder 是否顯示,將文本輸入框的 value 值設置爲 placeholder 的值來模擬顯示 placeholder 的狀態。再添加上事件處理程序,當文本輸入框獲取焦點時若是 value 的值爲 placeholder 則清空文本輸入框;當文本輸入框失去焦點時若是 value 值爲空則將 placeholder 的內容賦給它,同時當 placeholder 顯示的時候應該給文本輸入框添加一個 class="placeholder" 用來設置樣式以區別是顯示的 placeholder 和仍是顯示的普通 value:

// 作點什麼事
$('input[placeholder]').on('focus', function() {
  var $this = $(this);
  if (this.value === $this.attr('placeholder') && $this.hasClass('placeholder')) {
    this.value = '';
    $this.removeClass('placeholder');
  }
}).on('blur', function() {
  var $this = $(this);
  if (this.value === '') {
    $this.addClass('placeholder');
    this.value = $this.attr('placeholder');
  }
});

這只是一個簡單的模擬實現,實際上還有不少須要處理的狀況。

處理密碼輸入框

若是須要處理 placeholder 的是個密碼輸入框,它的 value 值會顯示爲圓點之類的字符,呈現幾個莫名其妙的圓點來做爲 placeholder 提示恐怕不妥,所以須要特殊對待一下,將密碼輸入框拷貝一份出來而後修改其 type 屬性爲 'text' 來替代顯示 placeholder,並把本來的密碼輸入框隱藏:

$('input[placeholder]').on('blur', function() {
  var $this = $(this);
  var $replacement;
  if (this.value === '') { // 失去焦點時值爲空則顯示 placeholder
    if (this.type === 'password') {
      $replacement = $this.clone().attr('type', 'text');
      $replacement.data('placeholder-password', $this);

      // 替代顯示的文本輸入框獲取焦點時將它刪掉,而且從新顯示原來的密碼輸入框
      $replacement.on('focus', function() {
        $(this).data('placeholder-password').show().focus();
        $(this).remove();
      });
      $this.after($replacement).hide();
      $this = $replacement;
    }
    $this.addClass('placeholder');
    $this[0].value = $this.attr('placeholder');
  }
});

對於 IE 8 來講它不支持修改 input 元素的 type 屬性,使用 jQuery 的 .attr() 方法來修改的話只會默默地失敗。此時,能夠新建一個文本輸入框,而後把密碼輸入框上的屬性賦給這個新建的文本輸入框:

try {
  $replacement = $this.clone().prop('type', 'text'); // 使用 .prop() 方法在 IE 8 下會報錯
} catch(e) {
  $replacement = $('<input>').attr({
    'type': 'text',
    'class': this.className // 還能夠賦予 id, name 等屬性
  });
}

須要注意的是 id 和 name 屬性不要重複了,能夠先用一個變量保存下來,再用 .removeAttr() 方法來刪除屬性。

處理表單提交

當表單提交的時候應該將那些正在顯示 placeholder 的文本輸入框過濾掉,畢竟沒有必要將那些沒有用的數據提交給服務器,在提交時候將那些文本輸入框的 value 值設爲空,提交以後再恢復成顯示 placeholder 的狀態:

$(document).on('submit', 'form', function() {
  var $input = $('.placeholder', this);
  $input.each(function() {
    this.value = '';
  });
  setTimeout(function() {
    $input.each(function() {
      this.value = $(this).attr('placeholder');
    });
  }, 10);
});

離開頁面時

當用戶離開頁面時也須要清空正在顯示 placeholder 的文本輸入框,防止瀏覽器記住文本輸入框當前的值,這裏能夠給 window 對象綁定一個 beforeunload 事件來處理:

$(window).on('beforeunload', function() {
  $('.placeholder').each(function() {
    this.value = '';
  });
});

另外還須要考慮的問題:

  • 使用代碼給一個文本輸入框賦值時,應該同時處理 placeholder 的狀態。
  • 使用代碼獲取一個正在顯示 placeholder 的文本輸入框的值的時候應該獲得的是一個空字符串。

如此多的問題也就是說不管 Polyfill 寫到如何極致,都很難與原生的功能相提並論,所以推薦的方式使用特性檢測技術僅針對不支持的瀏覽器作 Polyfill,而 Polyfill 的功能應儘量地向原生功能看齊。

總結

將全部樣式總結起來:

input::-webkit-input-placeholder,
textarea::-webkit-input-placeholder {
  color: #999;
}

input::-moz-placeholder,
textarea::-moz-placeholder {
  color: #999;
  opacity: 1;
}

input:-ms-input-placeholder,
textarea:-ms-input-placeholder {
  color: #999;
}

.placeholder {
  color: #999;
}

input:focus::-webkit-input-placeholder,
textarea:focus::-webkit-input-placeholder {
  color: transparent;
}

input:focus::-moz-placeholder,
textarea:focus::-moz-placeholder {
  color: transparent;
}

Polyfill 能夠直接使用這個 jQuery 插件 mathiasbynens/jquery-placeholder,已經至關完善了。

參考資料

相關文章
相關標籤/搜索