Каков наиболее точный способ эмулировать атрибут заполнителя в браузерах, которые изначально его не поддерживают?

Недавно я просматривал решения jquery/javascript для эмуляции атрибута placeholder, но ни одно из них, похоже, не подходит. Общие проблемы:

  • Встроенная поддержка браузера не используется в первую очередь
  • Форма может фактически публиковать значение текста-заполнителя.
  • Невозможно распознать текст-заполнитель и ввод пользователя, если значения идентичны (Эй, мой адрес электронной почты: [email protected]!! Почему он не отправляется? Эй, я хотел найти строку «Поиск», но это не пускает!)
  • Трудно стилизовать или отличить обычный текст от текста-заполнителя.
  • Навязчивая реализация (почти всегда. Я не хочу добавлять 2 div на вход и таблицу стилей для этого)
  • Просто не работает. По крайней мере, 2 из них на веб-сайте jQuery.

Я немного поиграл, пытаясь заставить это работать правильно (взяв несколько подсказок из кода, который я уже видел), но он все еще нуждается в доработке. В частности, форма может публиковать значение заполнителя. Комментарии и модификации jsfiddle приветствуются. (Примечание. Демонстрацию следует просматривать в браузере без поддержки заполнителей) Большинство кода, который я видел, принимает значение placeholder и вставляет его в сам ввод, что приводит к этой проблеме, я чувствую, что там должен быть лучший способ.

Есть ли хорошее чистое решение, которое действительно работает?

РЕДАКТИРОВАТЬ: я хочу подчеркнуть это: он должен вести себя так, как вы видите его в браузерах, которые изначально поддерживают его в максимально возможной степени, и быть настолько ненавязчивым, насколько это возможно, как показано в моем текущем коде, который не требует ничего, кроме включения сценария и использования placeholder, как обычно для браузеров, которые его поддерживают.

ОБНОВЛЕНИЕ: Текущее решение @DA - это несколько исправлений ошибок, далеких от совершенства (см. комментарии), хотелось бы, чтобы это собралось на 100% и позорило весь плохой и глючный код в сети.

ОБНОВЛЕНИЕ: это работает с парой модификаций кода DA, но все еще не идеально, в основном в отношении динамически добавляемых полей ввода и существующих привязок submit(). Всем спасибо за помощь, я пока решил, что оно того не стоит. Я знаю несколько человек, которые обязательно воспользуются этим. Это хороший трюк, но для меня не стоит даже 1% вероятности того, что форма отправит что-то, для чего она не предназначена, или неправильно прочитает пользовательский ввод. Эта небольшая функция просто не стоит головной боли, IE и друзья могут просто справиться с ней, или ее можно реализовать в каждом конкретном случае, если она действительно нужна, например, если этого требует клиент. @DA еще раз спасибо, это лучшая реализация, которую я видел.

ЗАКЛЮЧЕНИЕ. Я думаю, что единственный способ решить все эти проблемы примерно так:

  • Скопируйте значение заполнителя в новый элемент уровня блока
  • Добавить новый элемент к входу
  • Рассчитайте высоту границы и отступ ввода и переместите новый элемент поверх него как можно ближе к положению, в котором должен появиться текст.
  • Используйте стандартные события размытия/изменения/фокуса, чтобы скрыть элемент, когда ввод имеет значение или сфокусирован

Таким образом, вам не нужно ничего делать при отправке или решать какие-либо другие проблемы, которые могут возникнуть. Извините, демо еще нет, мне нужно вернуться к работе, но я приберегу окончательное редактирование, когда все будет готово.


person Wesley Murch    schedule 07.04.2011    source источник
comment
Если вы действительно хотите что-то, что соответствует всем этим пунктам, это легко написать: Просто создайте эту структуру: <div style="position:relative;"><span style="position:absolute;top:0px;left:0px;"/><input style="background:transparent;" /></div> и установите для содержимого диапазона текст «заполнителя», когда ввод пуст.   -  person Mark Kahn    schedule 07.04.2011
comment
@cwolves: Что-то похожее на это сейчас начинает казаться лучшим решением.   -  person Wesley Murch    schedule 07.04.2011
comment
Одна небольшая проблема заключается в том, что мы обнаружили, что на некоторых мобильных устройствах может быть сложно передать фокус с наложенного текста на поле ввода. Что происходит, так это то, что вы можете передать фокус, но клавиатура не всегда срабатывает, так как на некоторых устройствах, которые не управляются событием фокуса, а конкретно от фактического события касания (чего не произошло бы, если бы вы нажали кнопку текст сверху, а не фактическое поле ввода).   -  person DA.    schedule 07.04.2011
comment
@DA: Хм... можно ли сделать событие касания trigger() событием фокуса? Мобильная разработка мне совершенно неизвестна, поэтому я уверен, что вы уже думали об этом. Кстати, я поместил ваш код в свою CMS, чтобы протестировать его, и именно здесь у меня возникли проблемы из-за различных других привязок отправки и элементов управления формой, созданных ajax, но распознавание пользовательского ввода действительно сработало.   -  person Wesley Murch    schedule 07.04.2011
comment
Ну, честно говоря, работа с браузерами BlackBerry похожа на работу с IE. Они просто отстой, поэтому в некотором смысле я ненавижу их приспосабливать, когда есть код, который работает везде. С положительной стороны, я думаю, что большинство сенсорных устройств (Windows 7 может быть большим исключением) будут правильно поддерживать заполнитель HTML5, так что, возможно, это не так уж важно.   -  person DA.    schedule 07.04.2011


Ответы (4)


«Большая часть кода, который я видел, принимает значение заполнителя и вставляет его в сам ввод, что приводит к этой проблеме»

Что ж, это в значительной степени то, что делает текст-заполнитель за кулисами (хотя он буквально не обновляет значение ввода).

Один из вариантов что-то вроде этого:

http://fuelyourcoding.com/scripts/infield/

Идея заключается в том, что вы перемещаете LABEL с помощью CSS, чтобы он находился поверх поля и выглядел как текст-заполнитель. Обратите внимание, что LABEL не обязательно совпадает с текстом-заполнителем. LABEL важно иметь, особенно для доступности, в то время как текст-заполнитель можно рассматривать как «текст справки» в дополнение к метке.

Другой вариант - это то, что вы делали, возьмите содержимое атрибута заполнителя и переместите его в значение самого ввода через JS.

Где вы зависаете, так это в том, что вы отправляете форму, прежде чем проверить, есть ли еще поддельный текст-заполнитель.

Чтобы обойти это, вы хотите прикрепить событие к отправке формы, которое перед отправкой формы сначала просматривает все поля ввода, получает их значения, сравнивает их с их атрибутами-заполнителями и, если они совпадают, устанавливает значение пустым.

ОБНОВИТЬ:

Чтобы решить проблему, которую вы подняли в комментарии к введенному пользователем значению, совпадающему с текстом заполнителя, вы можете сделать что-то вроде этого:

$('input[placeholder]').each(function(){

 if($(this).val() === $(this).attr('placeholder')){
      // in the odd situation where the field is prepopulated with
      // a value and the value just happens to match the placeholder,
      // then we need to flag it
      $(this).data('skipCompare','true');
  }    

// move the placeholder text into the field for the rest of the blank inputs
   if($(this).val().length===0){
       $(this).val($(this).attr('placeholder'))
   }

// move the placeholder text into the field for the rest of the blank inputs
  if($(this).val().length===0){
      $(this).val() = $(this).attr('placeholder');
  }


  $(this)
     .focus(function(){
           // remove placeholder text on focus
           if($(this).val()==$(this).attr('placeholder')){
               $(this).val('')
           }
     })
     .blur(function(){
         // flag fields that the user edits
         if( $(this).val().length>0 && $(this).val()==$(this).attr('placeholder')){
             $(this).data('skipCompare','true');
         }
         if ( $(this).val().length==0){
             // put the placeholder text back
             $(this).val($(this).attr('placeholder'));
         }
     })


 })

 $('#submitButton').click(function(){
   $('input[placeholder]').each(function(){
     if( !($(this).data('skipCompare')) && $(this).val() == $(this).attr('placeholder')     ){
         $(this).val('');
     };
     alert($(this).val());
   })
})

Уже поздно, и я устал, так что все это может быть совершенно неправильно. Гарантий не дали. ;)

person DA.    schedule 07.04.2011
comment
Это довольно хорошо с точки зрения пользователя, но как есть, это портит ваши метки, что во многих отношениях плохо. Я хотел бы увидеть модифицированную версию этого, которая, возможно, вместо этого клонирует этикетку. Надо будет посмотреть повнимательнее, спасибо. - person Wesley Murch; 07.04.2011
comment
Что вы имеете в виду под "беспорядок с вашими ярлыками"? - person DA.; 07.04.2011
comment
Я имею в виду, что он удаляет ваши теги <label>, чтобы использовать их в качестве заполнителя. Я все еще читаю, так что это может быть просто поведение по умолчанию, и есть возможность отключить его. - person Wesley Murch; 07.04.2011
comment
Извините, может я что-то перепутал. Мой ответ говорит о двух разных вещах. Один из них заключается в использовании тега label и размещении его поверх поля. Это все еще тег label, вы просто перемещаете его через CSS. Другой — это то, что вы создавали, где вы используете JS для перемещения значения атрибута-заполнителя во входное значение. Я думаю, что оба имеют свои достоинства и полезны в немного разных контекстах. - person DA.; 07.04.2011
comment
Цель состоит в том, чтобы эмулировать нативную реализацию браузера (как вы могли бы видеть, например, в FF4 или Chrome), что этот код делает не очень хорошо. - person Wesley Murch; 07.04.2011
comment
Нам понадобится больше отзывов от вас, чем «не очень хорошо». Что конкретно у него не очень получается? Я взялся за полный кусок jQuery, чтобы заставить это работать. Дайте мне знать, что, по вашему мнению, может отсутствовать или не работать. Хотелось бы доработать по мере необходимости. - person DA.; 07.04.2011
comment
Ну, во-первых, что, если мой ярлык «Пожалуйста, введите дату», и я хочу, чтобы мой заполнитель говорил ГГГГ-ММ-ДД, это решение не работает. - person Wesley Murch; 07.04.2011
comment
Ну, во-первых, это был jQuery, поэтому вам нужно, чтобы jsFiddle загружал jQuery. Но... у меня тоже были опечатки. Вот рабочий код. Я обновлю свой ответ: jsfiddle.net/65sNw/1 - person DA.; 07.04.2011
comment
Ага, моя ошибка, я забыл загрузить jQuery! Видел ваш новый пример, все еще страдает от неспособности распознать пользовательский текст по сравнению с текстом заполнителя (введите текст заполнителя в поле ввода и потеряйте фокус, ввод будет очищен). В противном случае это работает довольно круто. +1. Извините, что так резко об этом говорю, я потратил слишком много времени на это сегодня, просеивая код сбоя и работая над ним самостоятельно. Я по-прежнему считаю, что пользователь, вводящий текст, соответствующий заполнителю, является серьезной проблемой, хотя и небольшой, но я бы предпочел вообще избежать этого. Это лучший ответ на данный момент, большое спасибо. - person Wesley Murch; 07.04.2011
comment
@DA: Только что вспыхнула лампочка из-за анального сохранения проблемы пользовательского текста и заполнителя: добавление значения заполнителя с кучей пробелов. Это хакерски, но определенно работает, и пробелы не будут видны в заполнителе. Пользователь искал Поиск? Да, может быть. Пользователь искал Поиск, никак (форматирование удалило мои лишние пробелы). Завтра обновлю, так устал... - person Wesley Murch; 07.04.2011
comment
@Mad ... кажется, что форма проверяет, ввел ли пользователь текст-заполнитель. Введите текст в первое поле и нажмите кнопку, вы увидите в предупреждении, что он захватывает текст, даже если он соответствует заполнителю. Событие клика было только для демонстрации... хотя я верю, что jQuery вызовет это событие независимо от того, как форма отправлена ​​(мне пришлось бы дважды проверить это). Не уверен, что дополнительные пробелы будут работать, но это идея (более одного пробела в HTML игнорируется... это может быть верно и для значений атрибутов) - person DA.; 07.04.2011
comment
Любой ответ на этот ответ будет пинговать меня. Спасибо за обновления! Обратите внимание, ваше окончательное решение доставит вам головную боль на мобильных устройствах. Мы столкнулись с этой же проблемой на ежевике и были вынуждены отказаться от идеи позиционирования текста над полем. Посмотрите еще раз на мой пример, он делает то, что вы хотите (я думаю), позволяя пользователю вводить тот же самый текст, что и заполнитель, и по-прежнему отправлять его. - person DA.; 07.04.2011
comment
Вы не можете получить доступ к данным поля пароля через JS AFAIK. - person DA.; 17.09.2012
comment
submit работал у меня, я только что прикрепил его ко всем $('input[type=submit]') - person E Ciotti; 26.03.2013

Плагин jquery, который я написал некоторое время назад:

(function(){
    $.fn.inactiveText = function(inactiveClass, defaultValue){
        var a, b;
        this
            .data    ('defaultValue', defaultValue)
            .addClass('inactive')
            .focus(a=function(){
                var $this = $(this);
                if($this.hasClass(inactiveClass)){
                    (valFn.apply($this) == defaultValue) && $this.val('');
                    (valFn.apply($this) != defaultValue) && $this.removeClass(inactiveClass);
                }
    //            $this.hasClass(inactiveClass) && $this.valueIs(defaultValue).val('').end().valueIsNot(defaultValue).removeClass(inactiveClass);
            })
            .blur(b=function(){
                var $this = $(this);
                this.value || $this.addClass(inactiveClass).val(defaultValue);
            });
        this.each(a);
        this.each(b);

        this.setDefaultValue = function(d){
            this.each(a);
            defaultValue = d;
            this.each(b);
        };

        return this;
    };

    var valFn = $.fn.val;

    $.fn.val = function(v){
        if(typeof(v) == 'undefined'){
            var val = valFn.apply(this, arguments);
            return val == this.data('defaultValue') ? '' : val;
        }

        return valFn.apply(this, arguments);
    };
})();

Определите класс css для неактивного стиля (например, серый текст), и все готово.

обновленная скрипта: http://jsfiddle.net/cwolves/At8cu/1/

person Mark Kahn    schedule 07.04.2011
comment
Не работает правильно, попробуйте заполнить одно из полей, очистить его и потерять фокус. Используйте Ваше имя в качестве значения по умолчанию value для поля имени, очистите поле имени вручную и потеряйте фокус, он скопирует заполнитель электронной почты в поле имени. Я опубликовал скрипт с этим, но он не сохранил мое обновление, все еще учась играть :) - person Wesley Murch; 07.04.2011
comment
Это только потому, что я не изменил селектор, он все еще использует $('[placeholder]'). Измените его на $('[name=email]') и все будет хорошо, извините :) - person Mark Kahn; 07.04.2011
comment
Тогда это попадает в категорию навязчивых, если мне нужно определить это для каждого поля. Близко, но не сигара. - person Wesley Murch; 07.04.2011
comment
затем измените его, чтобы он читался как .attr('placeholder'), не так уж сложно :) В любом случае вы должны определить его для каждого поля, я просто решил сделать это в JS, а не во встроенном HTML, в основном потому, что проект, для которого я написал это, имел переключатели языка JS. - person Mark Kahn; 07.04.2011
comment
Извините, я не собираюсь делать все за вас! Кроме того, вы, вероятно, предпочтете метод, который я описал в ответе на ваш первоначальный вопрос, а я этого не пишу :) - person Mark Kahn; 07.04.2011

Предположим, что jQuery определен.

$(document).ready(function() {
    // Use native functionality if possible
    if (document.createElement('input').placeholder !== undefined) return;
    // Fallback code
    $('form').each(function(i, form) {
        var submitted = false;
        $(form).find('input[placeholder]').each(function(j, input) {
            addPlaceholder.call(input);
            $(input)
                .focus(removePlaceholder)
                .blur(addPlaceholder)
                ;
        });
        $(form).submit(function() {
            submitted = true;
            $(this).find('input[placeholder]').each(function(j, input) {
                removePlaceholder.call(input);
            });
        });
        function addPlaceholder() {
            if (!submitted && '' === $(this).val()) {
                $(this).val($(this).attr('placeholder')).addClass('placeholder');
            }
        }
        function removePlaceholder() {
            if ( $(this).hasClass('placeholder') &&
                 $(this).attr('placeholder') === $(this).val()
                 ) {
                $(this).val('').removeClass('placeholder');
            }
        }
    });
});

Оформите заполнитель с помощью CSS, чтобы он выглядел одинаково во всех браузерах:

            :-moz-placeholder { color: #bbb; opacity: 1; }
           ::-moz-placeholder { color: #bbb; opacity: 1; }
       :-ms-input-placeholder { color: #bbb; }
  ::-webkit-input-placeholder { color: #bbb; }
                 .placeholder { color: #bbb; }
person blah    schedule 26.09.2014

person    schedule
comment
Я только что кратко взглянул на все это, все они (я полагаю) потерпят неудачу в field.val(), предоставив вам текст-заполнитель вместо ''. - person Mark Kahn; 07.04.2011
comment
атрибут placeholder может быть там независимо от того, делает ли с ним что-либо браузер. Таким образом, когда вы запускаете проверку, сравните значение поля со значением атрибута-заполнителя этого поля. Если они совпадают, можно считать, что поле пустое. - person DA.; 07.04.2011
comment
Я проверил эти ссылки, и все они в какой-то момент терпят неудачу. @DA: Строго говоря, см. пункт 3 в моем вопросе, и этот подход также попадает в категорию навязчивых. - person Wesley Murch; 07.04.2011
comment
Это кажется чрезвычайно редким граничным случаем, когда ваш текст-заполнитель также будет текстом, введенным пользователем. Тем не менее, вы можете обойти это. Onblur, проверьте значение поля. Если он есть, пометьте поле как «данные пользователя», а затем игнорируйте сравнение с заполнителем при отправке. - person DA.; 07.04.2011
comment
@DA, я уже контролирую это в скрипке, которую я разместил в своем вопросе, пожалуйста, посмотрите. - person Wesley Murch; 07.04.2011
comment
В вашем скрипте похоже, что вы назначаете ПУСТЫЕ данные только тем полям, которые имеют значение по умолчанию, и это все, что вы проверяете при отправке. Вы также должны сравнить все заполнители с входными значениями. - person DA.; 07.04.2011
comment
Нет, только сначала, потом использует change() для присвоения данных. Он правильно очищает поля, как я прошу, но по-прежнему отправляет данные сообщения, но я еще не тестировал его без отправки js. Это единственная точка отказа в моем коде, о которой я знаю, но, конечно же, очень важная! - person Wesley Murch; 07.04.2011