Javascript-форум (https://javascript.ru/forum/)
-   Ваши сайты и скрипты (https://javascript.ru/forum/project/)
-   -   Прозрачный полифил атрибута placeholder (https://javascript.ru/forum/project/52888-prozrachnyjj-polifil-atributa-placeholder.html)

danik.js 11.01.2015 20:19

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

input.value = '';
input.placeholder = 'placeholder';
alert(input.value); // пустая строка, не смотря на то, что в инпуте отображается 'placeholder'


Работает в IE8-9. В других браузерах - не знаю. Но знаю что подход работает в опере последней старой и в файрфоксе последнем. А значит, скорее всего будет работать и в старых версиях этих браузеров. В хроме облом. Хотя есть некоторые мысли, возможно и получится.
IE6-7 вот в пролете, да. Но это не многих огорчит.

Вот набросок:
<!DOCTYPE html>
<meta charset="utf-8" />
<style>
    input.placeholder{
        color: #999;
    }
</style>
<form action="">
    <p><input type="text" placeholder="abc" /></p>
</form>
<script>
    var input = document.getElementsByTagName('input')[0];
    var object = 'value' in input.constructor.prototype ? input.constructor.prototype : input;
    var nativeValueDescriptor = Object.getOwnPropertyDescriptor(object, 'value');
    input._value = input.value;
    input._placeholder = input.getAttribute('placeholder') || '';
    input.removeAttribute('placeholder'); // для теста в новых браузерах
    input._placeholderVisible = false;
    function showPlaceholder() {
        input._placeholderVisible = true;
        input.className += ' placeholder';
        nativeValueDescriptor.set.call(input, input._placeholder);
    }
    function hidePlaceholder() {
        input._placeholderVisible = false;
        input.className = (' ' + input.className + ' ').replace(/\s+placeholder\s+/);
        nativeValueDescriptor.set.call(input, '');
    }
    var phDescriptor = {
        set: function(value) {
            this._placeholder = value;
            if (this._placeholderVisible)
                nativeValueDescriptor.set.call(input, this._placeholder);
        },
        get: function() {
            return this._placeholder;
        }
    };
    var valueDescriptor = {
        set: function(value) {
            input._placeholderVisible = false;
            nativeValueDescriptor.set.call(this, value);
        },
        get: function() {
            if (this._placeholderVisible)
                return '';
            return nativeValueDescriptor.get.call(this);
        }
    };
    Object.defineProperty(input, 'value', valueDescriptor);
    Object.defineProperty(input, 'placeholder', phDescriptor);
    if (!input._value) {
        showPlaceholder();
    }
    input.onfocus = function(e) {
        if (this._placeholderVisible)
            hidePlaceholder();
    };
    input.onblur = function(e) {
        if (!nativeValueDescriptor.get.call(this))
            showPlaceholder();
    };
</script>


Почему-то в IE input.getAttribute('placeholder') дает null
Причем setAttribute на самом деле устанавливает свойство, а не атрибут.
Наверно в IE не нужно ставить setter на placeholder, достаточно слушать propertychange.

caetus 15.01.2015 11:20

Вот моя реализация , если не брать к вниманию Кросс-браузерное добавление событий, получается 20 строк -+.
Емулятор ie показывает что даже в 5 работает , только я что то сомневаюсь ...

<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
<meta charset="utf-8"> 
<style>
input {
color: silver;
}
</style>
</head>

<div id="placeholder">
<input type="text"  data-pl="abc" value='abc'>
<input type="text"  data-pl="abc1" value='abc1'>
<input type="text"  data-pl="abc11" value='abc11'>
<input type="text"  data-pl="abc12" value='abc12'>
</div>

<div id="place">
<input type="text"  data-pl="abc1" value='abc1'>
<input type="text"  data-pl="abc2324" value='abc2324'>
</div>
[JS run]
<script>
function addevent(obj, e, h) {if (obj.addEventListener) { obj.addEventListener(e, h, false);} else if (obj.attachEvent) { obj.attachEvent('on'+e, h);} else { obj['on'+e]=function() { h();};}} function delevent(obj, e, h) { if (obj.removeEventListener) { obj.removeEventListener(e, h, false);} else if (obj.detachEvent) { obj.detachEvent('on'+e, h);} else {obj['on'+e]=null;}}

var fn = function (elem) {
  var placeholder = document.getElementById(elem);
	
  var o = {
    focus: function (e) {
      e = e.target || e.srcElement;
      if(!e.type == 'text') return;
      if(e.value == e.getAttribute('data-pl')) {
        e.value = '';
        e.style.color = 'black'
      }
      addevent(e, 'blur', o.blur);
    },
    
    blur: function (e) {
      e = e.target || e.srcElement;
      if(e.value === '' || e.value == ' ') {
        e.value = e.getAttribute('data-pl');
        e.style.color = 'silver';
      }
      delevent(e, 'blur', o.blur);
     }
  }
  addevent(placeholder, 'click', o.focus);
}
var place1 =  fn('placeholder')
var place2 = fn('place')

</script>
[/JS]
</html>

Deff 15.01.2015 13:05

Цитата:

Сообщение от caetus
Емулятор ie показывает что даже в 5 работает

В 7 и в 6 работает, про пятую не наю

ruslan_mart 15.01.2015 18:29

А кому-нибудь приходила в голову такая идея: при потере фокуса (blur), если value пустой, то сверху на поле накладывать div или span с текстом? :)

BETEPAH 16.01.2015 00:25

Ruslan_xDD,
проще не сверху, а снизу, сделав прозрачным поле.
А если бы еще был селектор пустоты поля, то можно было бы переложить на css работу по скрытию/показу "плейсхолдера"

melky 16.01.2015 06:35

Цитата:

Сообщение от Ruslan_xDD (Сообщение 351560)
А кому-нибудь приходила в голову такая идея: при потере фокуса (blur), если value пустой, то сверху на поле накладывать div или span с текстом? :)

я так делал - нужно было placeholder показать через анимацию.

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

ruslan_mart 16.01.2015 09:19

BETEPAH,

<!DOCTYPE HTML>
<html>
  <head>
    <style type="text/css">
      input + span {
        color: #AAA;
        display: none;
        font-style: italic;
        line-height: 20px;
        margin-left: -165px;
        position: absolute;
        
      }
      input:not(:focus):invalid + span {
        display: inline;
      }
    </style>
  </head>
  <body>

    <input required="required" type="text">
    <span>Placeholder</span>

  </body>
</html>

danik.js 16.01.2015 09:50

Цитата:

Сообщение от caetus
Вот моя реализация

Да обычные реализации есть давно. Я же предлагаю реализацию DOM-интерфейса - свойство input.placeholder (не очень полезно) и адекватный input.value (вот это уже важно).
Для jQuery есть плагин, он перехватывает функцию val(). Хотелось бы тоже самое, но для ванильного js.
Цитата:

Сообщение от Ruslan_xDD
на поле накладывать div или span с текстом?

Есть и такой полифил. Решает почти все проблемы: с value, с type=password, с отправкой формы, но имеет и свои минусы. Тоже в планах.

BETEPAH 16.01.2015 10:34

Цитата:

Сообщение от Ruslan_xDD
input:not(:focus):invalid + span

Это сработает там, где и плейсхолдер нативно работает. IE9- в пролёте.

caetus 16.01.2015 13:02

если я тебя правильно понял , ты хочешь вмести с placeholder сразу проверку всей формы ?

danik.js 16.01.2015 13:47

Все что я хочу - подключить некий magicPlaceholder.js и забыть об этом.
Далее работать как обычно - из js обращаться к input.value, менять input.placeholder (в принципе), в том числе и через атрибуты. Причем с динамически созданными инпутами все также должно работать.
В IE9 по крайней мере это все реализуемо. Уже почти сделал.
В IE8 возможно тоже, но будут некоторые ограничения.

caetus 16.01.2015 16:28

<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
<meta charset="utf-8"> 
<style>
#canvas {
	border: solid black 1px;
	display: block;
}

</style>
<body>
<input type="text" placeholder="dasda">
<input type="text" placeholder="dasda" >
<input type="text" placeholder="dasda" >


<script>
[JS]
function addevent(obj, e, h) {if (obj.addEventListener) { obj.addEventListener(e, h, false);} else if (obj.attachEvent) { obj.attachEvent('on'+e, h);} else { obj['on'+e]=function() { h();};}} function delevent(obj, e, h) { if (obj.removeEventListener) { obj.removeEventListener(e, h, false);} else if (obj.detachEvent) { obj.detachEvent('on'+e, h);} else {obj['on'+e]=null;}}



var fn = function(){

var inp = document.querySelectorAll('input[type="text"');


for(var i = 0; i < inp.length; i++) {
	inp[i].setAttribute('data-pl', inp[i].getAttribute('placeholder'));
	inp[i].value = inp[i].getAttribute('placeholder');
}
  var o = {
    focus: function (e) {
      e = e.target || e.srcElement;
      if(!e.type == 'text') return;
      if(e.value == e.getAttribute('data-pl')) {
        e.value = '';
        e.style.color = 'black'
      }
      addevent(e, 'blur', o.blur);
    },
    
    blur: function (e) {
      e = e.target || e.srcElement;
      if(e.value === '' || e.value == ' ') {
        e.value = e.getAttribute('data-pl');
        e.style.color = 'silver';
      }
      delevent(e, 'blur', o.blur);
     },
     createPlaceholder: function (place) {
     var input = document.createElement('input');
           input.type = 'text';
           if('placeholder' in document.createElement('input') input.placeholder = place;
           else input.setAttribute('data-bl', plc);

           return input;
	 },
	 event: function () {
          if('placeholder' in document.createElement('input')) return;
	 	addevent(document.body, 'click', o.focus);
		addevent(document.body, 'blur', o.blur);
	 }
  }
o.event();
return o;
};

var fn1 = fn()
console.log(fn1)


[/JS]
</script>

</html>


через createPlaceholder можно создавать смело input.

caetus 16.01.2015 16:34

у меня работает ie8+

danik.js 16.01.2015 19:39

caetus, ты не правильно понял.

В общем, вроде бы работает: http://learn.javascript.ru/play/rc5dBb

Работает в IE8-9 и в последнем Firefox'е (отключил проверку для теста), надеюсь в старых версиях тоже получится.
Пока не решил проблему с автозаполнением браузера по F5.

danik.js 24.01.2015 11:49

Короче, все удалось с таким старьем как Firefox 3.6 и Opera 10.6.
В webkit-based браузерах облом - в них фэйковые дескрипторы нативных свойств DOM-элементов.

Автозаполнение тоже победил. Чуть поправлю код и залью на гитхаб. Правда опоздал я на пару годков с этой темой))


Часовой пояс GMT +3, время: 11:06.