placeholder插件詳解


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

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

如果我們直接用 .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 子空間"。

接下來我們來看源碼:

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方法了

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

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

} 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) {}
}

 

 

 

加油!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM