image
Понадобилось сделать многострочный placeholder в textarea. Выяснилось, что Firefox, в отличие от всех других современных браузеров, не поддерживает перенос строки в элементе placeholder. Хотя делает он это в соответствии с W3C спецификацией — радости это не добавляет.

Все нагугленные решения не понравились. Ставить JQuery-плагины только ради переноса строк в Firefox не хотелось. Решил попробовать сделать свой placeholder во вспомогательном блоке. В итоге получилось вот такое простое решение, которое работает во всех браузерах и предоставляет широкие возможности для кастомизации placeholder.

Стандартное поведение реализуется с помощью jQuery (используется у меня в проекте, при необходимости легко заменяется чистым JS). Если вам нравится скрывать placeholder, когда поле попадает в фокус, то можно обойтись только CSS.

Посмотреть пример на jsfiddle.

Обновленный пример

UPD
Спасибо gwer за подсказки.

Изменения и уточнения:
  • на чистом CSS работать не будет, без JS не обойтись
  • в JS нужно использовать событие 'input'. Тогда placeholder исчезает и при вставке из буфера обмена через контекстное меню.


<div id="textAreaWrap">
    
<textarea id="textArea"></textarea>

<!-- Check here. If textarea has content - set for this div attribute style="display: none;" -->
<div id="placeholderDiv">Multiline textarea<br> placeholder<br><br>that works in Firefox</div>
    
</div>

#textAreaWrap {
    position: relative;
    background-color: white;
}

#textArea {
    position: relative;
    z-index: 1;
    width: 350px;
    height: 100px;
    min-height: 100px;
    padding: 6px 12px;
    resize: vertical;
    background-color: transparent;
    /* When set background-color: transparent - Firefox  displays
    unpleasant textarea border. Set border style to fix it.*/
    border: 1px solid #a5a5a5;
}

#placeholderDiv {
    position: absolute;
    top: 0;
    padding: 6px 13px;
    color: #a5a5a5;
}


$(document).on('input', '#textArea', function () {
	
        if ($('#textArea').val()) {
            $('#placeholderDiv').hide();
        } else {
            $('#placeholderDiv').show();
        }      
});

Комментарии (15)


  1. Ninjak
    16.10.2015 14:51
    +1

    Хрень какая-то… если кликнуть по плэйсхолдеру, то фокус не сработает на текстареи… если так и делать, то плэйсхолдер надо делать лэйбелом и указывать for. Также не понятно, почему только в ФФ? В других браузерах ЦСС нынче не работает? ;)


    1. gwer
      16.10.2015 15:26

      Плейсхолдер расположен под textarea (z-index), потому фокус ловится там, где надо.

      Работает и в FF, и в прочих браузерах, в отличие от решения «из коробки», которое FF игнорирует. Собственно, отличие от «коробочного» решения заключается в поддержке лисой, что и отражено в заголовке.


  1. gwer
    16.10.2015 15:23

    Надо вешаться на другое событие. С keydown некорректно будет работать при вставке из буфера обмена через контекстное меню. Используйте событие input.

    Непонятны причины использования setTimeout. Он зачем вдруг понадобился?

    Для обертки не помешает overflow: hidden на случай, когда плейсхолдер вдруг окажется больше, чем textarea.

    Еще можно обойтись без background-color: transparent и сопутствующих проблем, если обертку сделать label'ом и не шаманить с z-index'ами.

    А решение на чистом CSS не будет работать. Плейсхолдер убирается только пока есть фокус на поле. Когда фокус пропадет, плейсхолдер вернется, несмотря на наличие текста.


    1. everythingispossible
      16.10.2015 16:23

      setTimeout был нужен при использовании keydown — без таймаута (даже нулевого) проверка поля происходит до того как как там появится символ.
      С использованием события input все стало работать и при вставке через контекстное меню + setTimeout стал не нужен.

      попробовал overflow: hidden – не получается, т.к. у блока placeholder стоит position: absolute
      Но я считаю это некритичным, т.к. при верстке такого специфического поля несложно учесть его размер,
      зная текст для placeholder и задать min-height: XXXpx;

      попробовал с label и без background-color: transparent и z-index
      тоже не получилось без них, т.к. при клике правой кнопкой мыши по placeholder появляется контекстное меню как при клике по блоку, а не по textarea.
      Возможно это как-то можно сделать, хотя мне пока непонятно зачем. Чем плохо использование background-color: transparent и z-index?

      С чистым CSS действительно не прокатывает.

      Пост обновил.
      Большое спасибо за подсказки!


      1. stcherenkov
        16.10.2015 18:33

        Используйте на плэйсхолдере всю мощь pointer-events: none.


    1. everythingispossible
      17.10.2015 14:27

      Таки да, overflow: hidden можно применить
      для этого у placeholder нужно к position: absolute и top: 0 добавить еще bottom: 0, right:0, left:0;

      Надо заметить, что цель использования overflow: hidden достигается не полностью. Размер обертки по вертикали почему-то получается на несколько пикселей больше textarea, а размер placeholder совпадает с размером обертки.
      Поэтому overflow: hidden прячет лишний текст placeholder, но часть первой спрятанной строки выглядывает под textarea.


  1. ds_pro
    16.10.2015 16:01

    Работает только с прозрачным textarea, это смех.


    1. gwer
      16.10.2015 16:13

      Комментом выше я написал, как можно реализовать без прозрачного textarea. Впрочем, не считаю это совсем уж большой проблемой. Просто стилей чуть больше. Бэкграунд textarea заменяется бэкграундом обертки.


      1. ds_pro
        16.10.2015 16:16

        А зачем? если можно сделать без велосипедов на JS.


        1. everythingispossible
          16.10.2015 16:24

          можно пример без велосипедов?


        1. gwer
          16.10.2015 16:24

          Динамически менять контент textarea? Согласен, может быть приемлемым. Только это опять будет велосипедом.

          Я на решение смотрел со своей колокольни: недавно реализовывал нечто подобное для однострочных инпутов в соответствиями с принципами Material. Там как раз после перебора пачки вариантов пришел к похожему, но это в силу ограничений, которых нет в задаче автора.


  1. dooza
    16.10.2015 19:22
    +1

    можно было обойтись без JS, с помощью css


    1. everythingispossible
      16.10.2015 19:30

      Я пробовал разные варианты. На чистом CSS пока не получилось.
      Если знаете как — подскажите.


  1. Keyten
    17.10.2015 16:21

    Попробуйте поиграться с разными word-wrap, break-word и т.п., вдруг поможет.

    И ещё, помнится, во времена IE6 плейсхолдеры реализовывали, просто изменяя value у текстовых полей, зачем накладывать див сверху? Критично, чтобы value был пустым, если в поле плейсхолдер?


  1. everythingispossible
    17.10.2015 17:13

    Готовые решения с изменением value у текстовых полей показались мне излишне громоздкими и неудобными.
    Зачем много кода, если можно обойтись обработкой одного события в JS.