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) {}
}
加油!