
Удобный кросс-браузерный placeholder с помощью jQuery
📅 Опубликовано:15 мая 2011
Это статья из старого первого блога, который сохранился только на archive.org. Все старые статьи помечены тегом 🚨 старое
Реализация подсказки в виде <label>
, спозиционированная над полем ввода, исчезающая при заполнении, плюс «умная очищалка» содержимого поля, появляющаяся/исчезающая при вводе/удалении содержимого поля, демо-страничка с тем, что должно в итоге получиться.
Есть в html5 такой полезный и удобный атрибут для полей ввода — placeholder.
Он позволяет без лишних усилий отобразить в поле ввода надпись-подсказку, в помощь пользователю. Но адекватно его поддерживают сейчас только некоторые браузеры, FireFox 4, Opera 11 и WebKit-браузеры.
Также, с точки зрения юзабельности, полезен и атрибут search, добавляющий к полю ввода при его заполнении, небольшой контрол в виде крестика для быстрой очистки введенной информации. Этот атрибут поддерживается еще меньшим количеством браузеров, на сегодняшний момент его поддерживают только WebKit-браузеры.
К тому же изменение внешнего вида (рамки, цвета, замена/добавление изображений) полей с этими атрибутами пока представляет собой адскую задачу с использованием селекторов атрибутов, к сожалению до конца не кросс-браузерную и выглядящую, мягко говоря, нелицеприятно :)
Самый простой, далеко не семантичный и самый распространенный способ задать плейсхолдер текстовому полю – добаввить инлайновые JavaScript-события прямо в теге поля ввода:
HTML
<input
type="text"
onblur="if (this.value==''){this.value='Поиск...'}"
onfocus="if (this.value=='Поиск...') this.value='';"
value="Поиск..." >
Семантикой здесь как видно и не пахнет, т.к. подсказки временно являются содержимым поля ввода. Куда более лучше было бы реализовать их при помощи <label>
как это сделано, к примеру, на страничке помощи компании Яндекс, <label>
здесь позиционируется абсолютно над текстовым полем и с помощью JS, при соответствующих действиях, убирается. Об этом способе подробно на одном из Яндекс-субботников рассказывал великий и ужасный :) Виталий Харисов. В своей лекции, приводя примеры с инлайновыми JS-событиями, он справедливо заметил, что для реального применения логику следует оформлять с помощью jQuery или на чистом JS.
Попытаемся соорудить подобное поле ввода с помощью jQuery. Для начала, аккуратно сверстаем соответствующие элементы.
HTML-код будет выглядеть следующим образом:
HTML
<form class="placeholder-form" action="#" method="post">
<div class="placeholder">
<label for="input-name" class="input-hint">
А как <span class="marked-text-false">вы</span>
относитесь к
<span class="marked-text-true">семантике</span>?
</label>
<input class="input-text" type="text" id="input-name" value="" >
</div>
</form>
CSS офомление:
CSS
.placeholder-form {
min-width:1000px;
padding:5em;
position:relative;
overflow:hidden;
}
.placeholder {
width:90%;
position:relative;
_zoom:1;
}
.placeholder label {display:block;}
.marked-text-true {color:#009933;}
.marked-text-false {color:#ff0000;}
.input-text {
display:block;
margin:.2em 0 .5em 0;
width:90%;
border:1px solid #ccc;
position:relative;
padding:.1em 60px .1em .3em;
}
.placeholder label,
.input-text {
font-size:3em;
line-height:1em;
color:#5e5e5e;
}
#js .placeholder label {
position:absolute;
top:.2em;
_top:.45em;
left:.3em;
cursor:text;
z-index:1;
}
Специально для разработчиков с параноидальной наклонностью делать все так, чтобы при отключенном JS в браузере ничего никуда не ехало и наша надпись-<label>
была, аккурат перед полем ввода, а не спозиционирована над ним, добавим небольшую строчку JS-кода:
JavaScript
document.documentElement.id = "js";
Этим незамысловатым буквосочетанием мы присваиваем тегу <html>
, айди js
. Таким образом, все селекторы, написанные после #js
, будут срабатывать только при включенном JS в браузере.
Вот так будет выглядеть наше поле ввода.
Теперь займемся оживлением подсказки.
С помощью событий jQuery .blur()
и .focus()
, срабатывающими когда элемент теряет фокус или приобретает фокус соответственно, опишем логику исчезания-появления подсказки, добавляя или удаляя у <label>
класс hide
, который будет скрывать подсказку заданием огромного отрицательного отступа слева: left:-9999em !important;
.
Для большей универсальности, если предположить, что таких полей с подсказками на странице может быть больше одного, будем обращаться к подсказке не непосредственно указывая ее класс, а с помощью метода .prev()
, к элементу в DOM-дереве,который предшествует полю ввода, то есть, к нашей подсказке. Причем по событию .blur()
мы будем убирать подсказку, только если метод .val()
ничего не возвращает, то есть в поле ввода пусто:
JavaScript
$('.input-text').blur(function() {
if ($(this).val() == '') $(this).prev().removeClass('hide');
});
$('.input-text').focus(function() {
$(this).prev().addClass('hide');
});
Также, учитывая перфекционистские наклонности автора :) и возможности браузеров (кроме Opera) драг-энд-дропить текст в поля ввода, продумаем и эту деталь.
Итак, если мы перетащим текст в какой-либо версии эксплорера или WebKit-браузере в текстовое поле – оно получит фокус и сработает событие .focus()
, поведение при котором уже было описано и подсказка исчезнет. Но в Mozilla FireFox этого почему-то не происходит и .focus()
не срабатывает. Мне в голову не пришло ничего более умного, чем проверять методом .mouseover()
, находится ли курсор с перетаскиваемым текстом над полем ввода и если поле ввода было не
пустым, принудительно прятать подсказку:
JavaScript
$('.input-text').mouseover(function() {
if ($(this).val() != '') $(this).prev().addClass('hide');
});
Некоторые браузеры отображают ранее заполненную информацию при простой перезагрузке страницы (не по ctrl+F5). В частности это делает Mozilla FireFox и интернет эксплореры. Поэтому нужно проверить, не пустое ли поле после перезагрузки страницы. В этом случае, тоже исходя из предположения, что полей ввода может быть больше чем одно, дабы обращаться к каждому полю ввода при помощи указателя this
, воспользуемся методом .each()
, который будет матчить все поля ввода с соответствующим классом:
JavaScript
$('.input-text').each(function(){
if($(this).val() != '') $(this).prev().addClass('hide');
});
Теперь вся jQuery-конструкция выглядит следующим образом:
JavaScript
$(document).ready(function() {
$('.input-text').each(function(){
if($(this).val() != '') $(this).prev().addClass('hide');
});
$('.input-text').blur(function() {
if ($(this).val() == '') $(this).prev().removeClass('hide');
});
$('.input-text').focus(function() {
$(this).prev().addClass('hide');
});
$('.input-text').mouseover(function() {
if ($(this).val() != '') $(this).prev().addClass('hide');
});
});
В итоге должно получиться нечто подобное.
Теперь доработаем немного удобность пользования полем с помощью CSS.
Как видно на тестовой страничке предыдущего примера, при перетаскивании текста (напомню, в браузере Opera такой возможности нет) подсказка загораживает основную часть поля ввода и курсор принимает соответствующий угрожающий значек :).
Приходится водить мышью и искать, где бы все-таки можно отпустить текст (К примеру в поле поиска Яндекс-почты так и сделано).
Казалось бы, все просто: убираем z-index:1;
у подсказки #js .placeholder label
, делаем прозрачным поле ввода, отменяя у него бэкграунд — background:none;
, но предательский курсор над подсказкой в эксплорерах прекрасно продолжает мешать драг-энд-дропу.
Но решение, как обычно странное, все же есть ;)
Специально для эксплореров задаем бэкграундом изображение — //background:url(‘bg.gif’);
, причем экслорерам все равно, есть оно на сервере или нет, подсказка уйдет под поле ввода в обоих случаях.
Решение с подкладкой подсказки под поле ввода имеет свои недостатки. К примеру, если необходим отличный фоновый цвет поля от окружающего фона сайта, придется мудрить с дополнительной оберткой, которой задавать нужный бэкграунд и это выйдет только в том случае, если внешний вид поля ввода, оформляется по-другому, отлично от стандартного вида.
Дополненный CSS-код:
CSS
.placeholder-form {
min-width:1000px;
padding:5em;
position:relative;
overflow:hidden;
}
.placeholder {
width:90%;
position:relative;
_zoom:1;
}
.placeholder label {display:block;}
.marked-text-true {color:#009933;}
.marked-text-false {color:#ff0000;}
.input-text {
display:block;
margin:.2em 0 .5em 0;
width:90%;
border:1px solid #ccc;
position:relative;
padding:.1em 60px .1em .3em;
background:none;
//background:url('bg.gif');
}
.placeholder label,
.input-text {
font-size:3em;
line-height:1em;
color:#5e5e5e;
}
#js .placeholder label {
position:absolute;
top:.2em;
_top:.45em;
left:.3em;
cursor:text;
}
.hide {left:-9999em !important;}
Итак, окончательный вариант подсказки.
Теперь займемся реализацией нашей «очищалки» содержимого поля.
Роль «очищалки» у нас будет выполнять <span>
с классом clear-text
. Разместим его сразу после текстового поля:
HTML
<form class="placeholder-form" action="#" method="post">
<div class="placeholder">
<label for="input-name" class="input-hint">
А как
<span class="marked-text-false">вы</span>
относитесь к
<span class="marked-text-true">семантике</span>?
</label>
<input class="input-text" type="text" id="input-name" value="" >
<span class="clear-text"></span>
</div>
</form>
И назначим ему соответствующие стили:
CSS
.clear-text {display:none;}
#js .clear-text {
position:absolute;
top:19px;
_top:29px;
right:42px;
width:30px;
height:30px;
background:url("images/clear-cross.png") no-repeat;
cursor:pointer;
z-index:1000;
display:none;
}
#js .clear-text:hover {background-position:0 -30px;}
#js .ct-show {display:block;}
С ховером для IE6 возиться не стал, да простят меня суровые его пользователи :)
Для показа/скрытия будем задавать/удалять класс ct-show
.
Для начала обозначим непосредственное действие по клику, которое будет выполнять наша «очищалка» – удалять содержимое текстового поля, ставить фокус на это поле и после этого скрывать саму себя за ненадобностью.
Как и в предыдущих вариантах, будем стараться сделать код универсальным, обращаясь к нужному полю посредством указателя this
используя методы .parent()
— родительский элемент — блок с класом placeholder
и метода .find()
, с помощью которого мы будем искать в текущем блоке placeholder
текущее поле ввода input-text
:
JavaScript
$('.clear-text').click(function() {
$(this).parent().find('.input-text').val('').focus();
$(this).removeClass('ct-show');
});
«Очищалка» должна появляться сразу после ввода первого символа, а также исчезать, после удаления последнего. Для этого воспользуемся событием .keyup()
— по «отжатию» кнопки клавиатуры. Не .keydown()
— потому что, тогда пришлось бы нажимать еще раз клавишу для появления или исчезновения «очищалки»:
JavaScript
$('.input-text').keyup(function() {
if($(this).val() != '') {
$(this).parent().find('.clear-text').addClass('ct-show');
}
else {$(this).parent().find('.clear-text').removeClass('ct-show');}
});
Также как и в первом случае с драг-энд-дропом, добавим появление «очищалки» в уже имеющееся условие специально для FF и WebKit:
JavaScript
$('.input-text').mouseover(function() {
if ($(this).val() != '') {
$(this).prev().addClass('hide');
$(this).parent().find('.clear-text').addClass('ct-show');
}
});
А также, для тех браузеров, которые после простой перезагрузки запоминают текстовые поля. Добавим в уже имеющуюся логику строчку для появления «очищалки» — если поле не пусто:
JavaScript
$('.input-text').each(function(){
if($(this).val() != '') {
$(this).prev().addClass('hide');
$(this).parent().find('.clear-text').addClass('ct-show');
}
});
Теперь вся jQuery-конструкция будет выглядеть так:
JavaScript
$(document).ready(function() {
$('.input-text').each(function(){
if($(this).val() != '') {
$(this).prev().addClass('hide');
$(this).parent().find('.clear-text').addClass('ct-show');
}
});
$('.input-text').blur(function() {
if ($(this).val() == '') $(this).prev().removeClass('hide');
});
$('.input-text').focus(function() {
$(this).prev().addClass('hide');
});
$('.input-text').mouseover(function() {
if ($(this).val() != '') {
$(this).prev().addClass('hide');
$(this).parent().find('.clear-text').addClass('ct-show');
}
});
$('.clear-text').click(function() {
$(this).parent().find('.input-text').val('').focus();
$(this).removeClass('ct-show');
});
$('.input-text').keyup(function() {
if($(this).val() != '') {
$(this).parent().find('.clear-text').addClass('ct-show');
}
else {$(this).parent().find('.clear-text').removeClass('ct-show');}
});
});
В итоге, помимо «умной подсказки», к нашему полю добавится еще и «умная очищалка». Страница с примером.
Еще один неприятный и непонятный пока мне момент с IE. При добавлении в JS-код изменений для «очищалок», с интернет эксплорерами начинает происходить странная штука, в любом поле ввода, если его не заполнять, а просто установить курсор, при простой перезагрузке данное поле будет «как-бы в фокусе», но при этом .focus()
срабатывать не будет, и подсказка будет отображена всеравно, пока не произойдет нажатие клавиши, или «перефокусировка» (.blur()
а потом опять фокус
в это поле). Для решения этой проблемы достаточно вызвать .blur()
для всех полей при перезагрузке страницы:
JavaScript
$('.input-text').each(function(){
$(this).blur(); // чтобы сбросить фокус со всех полей при перезагрузке страницы в ie
if($(this).val() != '') {
$(this).prev().addClass('hide');
$(this).parent().find('.clear-text').addClass('ct-show');
}
});
Работу нескольких полей, одного только с подсказкой, а двух других — с подсказкой и «очищалками», можно посмотреть на этой демо-странице.
Скачать архив всех тестовых страниц и демо-страницу с комментариями в коде, можно отсюда.