placeholder插件詳解

placeholder插件是用來實現input或者textarea文本框顯示默認文字的功能,當得到焦點時,默認文字消失。用戶按刪除鍵,把輸入的字符刪除掉,並失去焦點時,默認文字又出現等功能。使用此插件,只須要$("input").placeholder();這input元素,必須有placeholder屬性,屬性值就是input默認顯示的文本,並且input元素中的class屬性值中沒有placeholder值,此插件也是基於jQuery的。jquery

再看源碼以前,我先講一個知識點:json

若是咱們直接用 .click() / .bind('click') 加上 .unbind('click') 來進行重複綁定,被 unbind 掉的將是綁定在元素上的全部click處理函數,潛在會影響到該元素在其餘第三方的行爲(好比,有另一個插件也綁定了該元素的click事件)。固然若是在bind的時候是顯式定義了function的名字的話,能夠在unbind的時候提供function的名字做爲第二個參數unbind其中一個處理函數,但實際應用中咱們極可能會碰到各類進行匿名函數綁定的狀況。瀏覽器

對於這種問題,jQuery的解決方案是使用事件綁定的命名空間。即在事件名稱後添加 .something 來區分本身這部分行爲邏輯範圍。
好比用 .bind('click.myCustomRoutine',function(){...}); 一樣是把匿名函數綁定到 click 事件(你能夠用本身的命名空間屢次綁定不一樣的匿名處理函數上去),當unbind的時候用 .unbind('click.myCustomRoutine') 便可釋放全部綁定到 .myCustomRoutine 命名空間的 click 事件,而不會解除其餘經過 .bind('click') 或另外的命名空間所綁定的click事件處理函數。
同時,使用命令空間還可讓你一次性 unbind 全部此命名空間下的事件綁定,經過 .unbind('.myCustomRoutine') 便可。
要注意的是,jQuery的命名空間並不支持多級空間。所以在jQuery裏面,若是用 .unbind('click.myCustomRoutine.myCustomSubone') ,解除的是命名空間分別爲 myCustomRoutine 和 myCustomSubone 的兩個並列命名空間下的全部 click 事件,而不是 "myCustomRoutine 下的 myCustomSubone 子空間"。緩存

接下來咱們來看源碼:ide

var isInputSupported = 'placeholder' in document.createElement('input');   //看此瀏覽器的input是否默認支持placeholder屬性,IE6-9不支持
var isTextareaSupported = 'placeholder' in document.createElement('textarea');  
var prototype = $.fn;   //jQuery的原型對象
var valHooks = $.valHooks;   //解決瀏覽器兼容性的對象,它裏面有不少屬性,好比:option,select,radio,checkbox,這些屬性是一個對象,具備set或get方法來解決瀏覽器兼容性問題。jQuery中針對這4個元素的value值進行了兼容性處理。
var hooks;
var placeholder;函數

if (isInputSupported && isTextareaSupported) {  //若是瀏覽器默認支持,就直接在jQuery的原型對象中添加placeholder方法,這樣jQuery對象均可以調用placeholder方法了this

  placeholder = prototype.placeholder = function() {
    return this;    //this其實指的就是jQuery對象,$("input")
  };spa

  placeholder.input = placeholder.textarea = true;  //給placeholder方法添加兩個屬性,屬性值都爲trueprototype

} else {插件

  placeholder = prototype.placeholder = function() {
    var $this = this;
    $this.filter(

      (isInputSupported ? 'textarea' : ':input') + '[placeholder]'   //若是支持input的palceholder,就是textarea[placeholder],若是不支持,就是:input[placeholder]。過濾出這些元素進行下一步操做,這些元素多是有placeholder屬性的textarea元素,也有多是有placeholder屬性的表單元素(:input選擇器選取表單元素)。

    ).not('.placeholder').bind(    //刪除class屬性值中有placeholder的元素

      {
        'focus.placeholder': clearPlaceholder,    //綁定focus和blur事件,命名空間爲placeholder,方便unbind解綁時,使用。
        'blur.placeholder': setPlaceholder
      }

    ).data('placeholder-enabled', true)   //給這些元素在緩存系統中添加placeholder-enabled爲true的數據

    .trigger('blur.placeholder');   //觸發blur.placeholder事件,執行setPlaceholder方法。也就是默認狀況下,鼠標焦點不在input框中,所以觸發input的blur事件
    return $this;
  };

  placeholder.input = isInputSupported;
  placeholder.textarea = isTextareaSupported;

  hooks = {    //定義局部變量hooks 

    'set': function(element, value) {   //若是是針對密碼框設置placeholder,那麼這裏的element就是新的input元素,若是針對的是普通的input框,那麼這裏的element就是這個普通的input元素,這裏的value是默認文本
      var $element = $(element);

      var $passwordInput = $element.data('placeholder-password');   //若是是密碼框,那麼新的input的placeholder-password值就是密碼框對象,若是是普通的input元素,這裏返回undefined。
      if ($passwordInput) {
        $passwordInput[0].value = value;   //設置密碼框的value值
      }

      if (!$element.data('placeholder-enabled')) {   //若是是普通的input元素,$element.data('placeholder-enabled')返回true,取反後會返回false,所以不會進入if語句。
        return element.value = value;
      }
      element.value = value;
      return $element;
    }
  };

  if (!isInputSupported) {   //若是瀏覽器不支持input的placeholder事件,就進入if語句
    valHooks.input = hooks;         //給jQuery添加input屬性對象,來解決瀏覽器input元素設置值與取值的兼容性問題,本來valHooks只有radio,checkbox,select,option這四個屬性,如今多了input。
  }
  if (!isTextareaSupported) {   //若是不支持textarea的placeholder事件,就進入if語句
    valHooks.textarea = hooks;  //針對textarea元素設置值與取值的兼容性問題的解決
  }

}

function args(elem) {    //elem是input元素,針對IE6-7
  var newAttrs = {};
  var rinlinejQuery = /^jQuery\d+$/;
  $.each(elem.attributes, function(i, attr) {    //取input元素的attributes屬性對象
    if (attr.specified && !rinlinejQuery.test(attr.name)) {   //若是屬性值是被用戶顯式設置的,那麼它的specified就會爲true,而後判斷屬性名不是jQuery的自定義屬性(在元素上調用data方法,往jQuery緩存系統中添加數據時,會在元素上添加一個以jQuery開頭+隨機數的一個自定義屬性)
      newAttrs[attr.name] = attr.value;      //把這些顯式設置的屬性值,保存到一個json對象中,並返回。
    }
  });
  return newAttrs;
}

function clearPlaceholder(event, value) {   //當鼠標焦點在input元素上時

  var input = this;
  var $input = $(input);
  if (input.value == $input.attr('placeholder') && $input.hasClass('placeholder')) {  //若是input的文本是默認文本,而且class屬性中有placeholder值時,進入if語句,若是用戶在input中輸入默認的文本,而後觸發blur事件,這時會把input的placeholder類刪掉,所以當你再次觸發focus事件時,不會進入if語句
    if ($input.data('placeholder-password')) {    //若是是密碼框,這裏的input實際上是inputNew,由於input已經被hide了。
      $input = $input.hide().next().show().attr('id', $input.removeAttr('id').data('placeholder-id')); //把inputNew隱藏,取到input元素,顯示出來,並恢復input元素的id。同時移除inputNew的id,並取得inputNew的緩存系統中placeholder-id的值(這個值就是以前input元素id的值),賦給input元素的id。
      $input.focus();  //input元素得到焦點(這時的input就是密碼框的input了)
    } else {   //若是不是密碼框
      input.value = "";   //就把input中的文本清空
      $input.removeClass('placeholder');   //並移除掉這個placeholder類
      input == safeActiveElement() && input.select();   //解決IE9下的一個bug,由於IE9下,若是是iframe中的元素得到了焦點,你調用document.activeElement,IE9會拋出錯誤。所以必須用try,catch來處理下,若是拋出錯誤,就不執行元素的select方法(此方法會選擇input框中的文本),若是沒拋出錯誤,就執行input的select方法(返回值是undefined).
    }
  }
}

function setPlaceholder() {   //失去焦點觸發blur事件
  var $replacement;
  var input = this;   //this指的就是input元素
  var $input = $(input);  //轉換成jQuery對象
  var id = this.id;   //取到input元素的id

  if (input.value == "") {  //若是input框中是空字符串
    if (input.type == 'password') {   //若是input是密碼輸入框
      if (!$input.data('placeholder-textinput')) { //input元素在jQuery緩存系統中是否有placeholder-textinput屬性值,沒有就進入if語句
        try {
          $replacement = $input.clone().attr({ 'type': 'text' }); //克隆一個input元素,咱們命名爲inputNew,並設置它的type屬性爲text。IE老版本會報錯,不能修改克隆出來的input的type值
        } catch(e) {
          $replacement = $('<input>').attr($.extend(args(this), { 'type': 'text' }));  //把這個input元素全部顯式設置的屬性取出來,而後擴展type屬性。而後把這些屬性所有賦值到新建立的input元素中,咱們命名爲inputNew
        }
        $replacement.removeAttr('name').data({    //移除掉inputNew元素的name屬性,並往jQuery數據緩存系統中添加數據
          'placeholder-password': $input, 
          'placeholder-id': id
        }).bind('focus.placeholder', clearPlaceholder);    //給這個inputNew元素綁定focus事件。
        $input.data({     //給input元素往jQuery緩存系統中添加數據
          'placeholder-textinput': $replacement,
          'placeholder-id': id
        }).before($replacement);   //在元素input前面添加inputNew元素
      }
      $input = $input.removeAttr('id').hide().prev().attr('id', id).show();  //移除input元素的id屬性,並隱藏,得到input元素前面的那個元素,也就是inputNew元素,而後設置它的id,並讓它顯示出來。
    }
    $input.addClass('placeholder');   //若是原來的input是密碼框,這裏的input就是inputNew,若是不是密碼框,這裏就是原來的input。
    $input.val($input.attr('placeholder'));  //這裏,若是瀏覽器的input或者textarea不支持placeholder事件,將調用上面定義的hooks來設置value值。具體請看hook的set方法。這裏爲何要新建一個新的input元素,是由於頁面上的input元素是密碼框,當你輸入文本進去時,會顯示***這種字符,可是默認狀況下,應該顯示默認文本,所以建立一個新的input元素,把密碼框隱藏,而後把默認文本放在新的input元素裏面。當用戶用鼠標去點擊時,新的input得到焦點時,就會把新的input元素隱藏,同時讓密碼框顯示出來,這樣用戶輸入的字符就會是***這種,若是不新建一個新的input,那麼默認文本在密碼框中顯示的是***。
  } else {   //若是input的value值不爲"",就移除input的placeholder類名
    $input.removeClass('placeholder');
  }
}

function safeActiveElement() {
  try {
    return document.activeElement;  //http://bugs.jquery.com/ticket/13378
  } catch (exception) {}
}

 

 

 

加油!

相關文章
相關標籤/搜索