Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Повесить сеттер на input.value (https://javascript.ru/forum/misc/30240-povesit-setter-na-input-value.html)

FINoM 28.07.2012 19:33

Повесить сеттер на input.value
 
Подскажите, как вызвать некую функцию не только при вызове события (keydown, select, change...), но и при обычном присваивании элементу формы некоегого значения:
input.value = 'blah'; // ← должна вызываться определенная функция
(на старые браузеры можно забить)

Обычный Object.defineProperty здесь, наверно, не прокатит, так как убивает стандартный геттер и сеттер.

Тот же вопрос интересен и касательно innerHTML.

devote 28.07.2012 19:38

<input id="lala" type="text" />
<script>
    var inp = document.getElementById( 'lala' );
    var descriptor = Object.getOwnPropertyDescriptor( inp, 'value' );
    var oldSetter = descriptor.set;
    descriptor.set = function( value ) {
        alert( "Попытка назначить: " + value );
        oldSetter.apply( this, arguments );
    }
    Object.defineProperty( inp, "value", descriptor );

    inp.value = "test";;
</script>

FINoM 28.07.2012 19:41

devote,
Uncaught TypeError: Invalid property.  A property cannot both have accessors and be writable or have a value, #<Object>


Object.getOwnPropertyDescriptor ( ROOT.name.field, 'value' )
 → 
configurable: true
enumerable: true
value: "Jubilee"
writable: true


Тут нет сеттера, как видно.

devote 28.07.2012 19:48

Я так понимаю это хром тупит с первым вариантом.
<input id="lala" type="text" />
<script>
    var inp = document.getElementById( 'lala' );

    Object.defineProperty( inp, "value", {
        configurable: true,
        enumerable: true,
        set: function( value ) {
            alert( "Попытка назначить: " + value );
            this.setAttribute( "value", value );
        },
        get: function() {
            return this.getAttribute( "value" );
        }
    });
 
    inp.value = "test";

    alert( inp.value );
</script>

FINoM 28.07.2012 19:50

devote, хм, как-то не догадался использовать атрибут. Спасибо.

FINoM 28.07.2012 19:51

Цитата:

Сообщение от devote
Я так понимаю это хром тупит с первым вариантом.

Он не тупит, просто дескриптор не содержит акцессоров.

devote 28.07.2012 19:54

Цитата:

Сообщение от FINoM
Он не тупит, просто дескриптор не содержит акцессоров.

это я понял, а опера содержит.

FINoM 28.07.2012 19:57

С innerHTML вопрос еще открыт. Здесь никак не заюзаешь атрибут.
Хотя, в принципе, можно погеморроиться: при срабатывании сеттера превращать полученную строку в DOM элементы, затем аппендить в искомый элемент, предварительно удалив каждый тег внутри.
При геттере преобразовывать элементы внутри в строку.
Но это уж слишком геморройно.

FINoM 28.07.2012 20:18

Вот она, рыба:
<div>Blah</div>
<span></span>
<script>
var div = document.querySelector( 'div' ),
  span = document.querySelector( 'span' );

div.addEventListener( 'DOMSubtreeModified', function() {
  span.innerHTML = 'fuck yea';
});

div.innerHTML = 'New HTML';
</script>

devote 28.07.2012 20:18

Цитата:

Сообщение от FINoM
Но это уж слишком геморройно.

ну почему геморойно, не так и геморно:

<div id="lala"></div>
<script>
    var div = document.getElementById( 'lala' );
 
    (function( div ) {
        var currentInnerHTML = div.innerHTML;
        Object.defineProperty( div, "innerHTML", {
            configurable: true,
            enumerable: true,
            set: function( value ) {
                alert( "Попытка назначить: " + value );
                var div = document.createElement("DIV");
                div.innerHTML = value;
                currentInnerHTML = div.innerHTML;
                // очищаем от всего что есть сейчас
                while( this.firstChild ) {
                    this.removeChild( this.firstChild );
                }
                // добавляем новое
                while( div.firstChild ) {
                    this.appendChild( div.firstChild );
                }
            },
            get: function() {
                return currentInnerHTML;
            }
        });
    })( div );
  
    div.innerHTML = "<b>test</b> <i>italic</i>";
 
    alert( div.innerHTML );
</script>

devote 28.07.2012 20:20

Цитата:

Сообщение от FINoM
Вот она, рыба:

тоже вариант :) А событие у все такое есть? тоесть у всех браузеров? вижу в опере работает, в хроме тож.. в других не знаю как будет.

devote 28.07.2012 20:22

хотя не, в опере у меня не пашет твой вариант, в 12-й не тестил, у меня щас 11.64... но зато в опере есть дескрипторы сеттер/геттер, тоесть с ней вообще все проще.

FINoM 28.07.2012 20:22

http://help.dottoro.com/ljrmcldi.php ← тут пишут, что работает везде, кроме оперы.

devote 28.07.2012 20:24

для оперы этот вариант канает:

<div id="lala"></div>
<script>
    var div = document.getElementById( 'lala' );
    var descriptor = Object.getOwnPropertyDescriptor( div, 'innerHTML' );
    var oldSetter = descriptor.set;
    descriptor.set = function( value ) {
        alert( "Попытка назначить: " + value );
        oldSetter.apply( this, arguments );
    }
    Object.defineProperty( div, "innerHTML", descriptor );
 
    div.innerHTML = "<b>test</b> <i>italic</i>";

    alert( div.innerHTML );
</script>

FINoM 28.07.2012 20:25

Цитата:

Сообщение от devote
для оперы этот вариант канает:

угу, спс

devote 28.07.2012 20:26

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

devote 28.07.2012 20:33

FINoM,
а ты видел че пишет w3c? http://www.w3.org/TR/DOM-Level-3-Eve...ubtreeModified

Цитата:

Warning! the DOMSubtreeModified event type is defined in this specification for reference and completeness, but this specification deprecates the use of this event type.

FINoM 28.07.2012 20:36

В жопу W3C :D

devote 28.07.2012 20:37

Цитата:

Сообщение от FINoM
В жопу W3C

:D

oneguy 28.07.2012 20:50

Вот, написал код, работает в IE, Firefox, Opera, но не в Chrome.
<!DOCTYPE html>

<html><head>

<title></title>

</head><body>
<p>asd</p>

<script type="text/javascript">
//основной код
var desc={
  get: function () {
    delete this.innerHTML;
    var res=this.innerHTML;
    Object.defineProperty(this, "innerHTML", desc);
    return res;
  },
  set: function (value) {
    this.oninnerHTML(value);
    delete this.innerHTML;
    this.innerHTML=value;
    Object.defineProperty(this, "innerHTML", desc);
  },
  configurable: true
};
function setInnerHTMLHandler(element) {
  Object.defineProperty(element, "innerHTML", desc);
}
//конец основного кода

var p=document.getElementsByTagName("p")[0];
setInnerHTMLHandler(p);
p.oninnerHTML=function (text) {
  alert(text);
}
p.innerHTML="123";
</script>
</body></html>

devote 28.07.2012 21:24

Работает везде
<!DOCTYPE html>
<html>
<body>
<div id="test"></div>
<div id="test2"></div>
<script>
function setInnerHTMLHandler( elem, setfn, getfn ) {
    var descriptor = Object.getOwnPropertyDescriptor( elem, "innerHTML" ),
        temp = document.createElement( 'div' ),
        currentHTML = elem.innerHTML;

    Object.defineProperty( elem, "innerHTML", {
        configurable: true,
        set: descriptor.set ? function( value ) {
            var result;
            if ( !setfn || ( result = setfn( value ) ) !== false ) {
                descriptor.set.call( this, result || value );
            }
        } : function( value ) {
            var result;
            if ( !setfn || ( result = setfn( value ) ) !== false ) {
                temp.innerHTML = result || value;
                currentHTML = temp.innerHTML;
                while( this.firstChild ) {
                    this.removeChild( this.firstChild );
                }
                while( temp.firstChild ) {
                    this.appendChild( temp.firstChild );
                }
            }
        },
        get: descriptor.get ? function() {
            var result;
            if ( !getfn || ( result = getfn() ) !== false ) {
                return result || descriptor.get.call( this );
            }
        } : function() {
            var result;
            if ( !getfn || ( result = getfn() ) !== false ) {
                return result || currentHTML;
            }
        }
    });
}


// тесты

var div = document.getElementById('test');

setInnerHTMLHandler( div, function( value ) {
    alert( "Попытка установить значение: " + value );
}, function() {
    alert( "Попытка получить значение" );
})

div.innerHTML = "Hello World!!!";
alert( div.innerHTML );


var div2 = document.getElementById('test2');
setInnerHTMLHandler( div2, function( value ) {
    alert( "Попытка установить значение: " + value );
    return "А вот и значение: " + value;
}, function() {
    alert( "Попытка получить значение" );
    return "Текущее не дам";
})

div2.innerHTML = "Hello World!!!";
alert( div2.innerHTML );


</script>
</body>
</html>

oneguy 28.07.2012 22:00

Цитата:

Сообщение от devote
Работает везде

В Фаерфоксе не работает: TypeError: descriptor is undefined, 14 строка.
Да и там, где работает, работает неправильно: innerHTML - это не единственный способ изменить содержимое элемента. Например appendChild не изменяет локальную переменную currentHTML.

oneguy 28.07.2012 22:02

У меня вопрос: нужно отследить только записи innerHTML или любые изменения внутри элемента?


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