Как отловить изменения у <select>?

323
08 января 2018, 12:01

Допустим у нас есть такой код и допустим пользователь оказался хитрым. Он вставил некий скрипт в код который изменяет значение у select при событии click :

document.addEventListener('click',function(){document.querySelectorAll("select")[0].value=document.querySelectorAll("option")[2].value},false)
<select onchange="console.log(event.target.value)"> 
  <option  value="Firefox Best!!!"  selected> Firefox Best!!! </option> 
  <option  value="Firefox Very best!!!" > Firefox Very best!!!</option> 
  <option  value="Chrome Best!!!" >Chrome Best!!!</option> 
</select>

Теперь selectElement.value="Chrome Best!!!" и так как отловить изменение value у select? Только не предлагать по таймингу.

Answer 1

Эта функциональность есть, но она находится в экспериментальной части на данный момент ECMAScript 7

Object.observe(obj, callback)

в вашем случае это должно работать так

var sel = document.getElementById("select1");
Object.observe(sel , () => console.log("changed"));

Документация - https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Object/observe

На данный момент эта функциональность почти не поддерживается браузерами.

Answer 2

Никак, пользователь вообще может даже не в скрипте это заменить а через f12 в режиме отладки страницы, кроме того он вообще может отключить все скрипты на странице, сделать любые замены через f12 и потом снова включить скрипты, и вы ничего не поделаете никакими "защитными" мерами.

Кроме того он может вообще получаемый от сервера html код перехватить например снифером или прокси и внедрить туда что угодно.

Есть и другие варианты, например использования эмуляции браузера, перекомпилированный браузер с дополнительным кодом или плагинами, и так далее.

В любом случае любой запрос от клиента на сервер надо валидировать, и вот это взломать уже мало реально, при учёте грамотного кода и надёжного сервера.

По сути варианта без валидации запросов на сервере в нормальном и защищённом коде быть не должно.

Answer 3

Вам необходимо использовать MutationObserver.

function changeElement() { 
  document.getElementById("id1").value = "zzz"; 
} 
 
var mo = new MutationObserver(observe); 
var el = document.getElementById("id1"); 
var config = { 
  attributes: true, 
  childList: true, 
  subTree: true 
}; 
mo.observe(el, config); 
 
function observe() { 
  alert("change"); 
}
<select onchange="console.log(event.target.value)"> 
  <option  value="Firefox Best!!!"  selected> Firefox Best!!! </option> 
  <option  value="Firefox Very best!!!" > Firefox Very best!!!</option> 
  <option id="id1" value="Chrome Best!!!" >Chrome Best!!!</option> 
</select> 
 
<a href="#" onclick="changeElement(); return false;">изменить</a>

Документация - https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver

Answer 4

Как оказалось в комментариях, вопорс сводится к тому, как сделать, чтобы событие change вызывалось при программном изменении значения в select'е.

Например, в следующем коде событие не возникает при нажатии кнопки:

document.querySelector("select").addEventListener('change', function (e) { 
  console.log("Changed to: " + e.target.value) 
}) 
 
document.querySelector("button").addEventListener('click', function (e) { 
  console.log("Button clicked: enter") 
  document.querySelector("select").value = 2 
  console.log("Button clicked: leave") 
})
<select> 
  <option value=1 selected>Option 1</option> 
  <option value=2>Option 2</option> 
  <option value=3>Option 3</option> 
</select> 
 
<button>#2</button>

Решением может быть перехват присваивания при помощи сеттера свойства.

Следующий код проверен в Chrome 43 (в IE надо будет поменять способ создания события):

var select = document.querySelector("select"); 
 
select.addEventListener('change', function (e) { 
  console.log("Changed to: " + e.target.value) 
}) 
 
var original = Object.getOwnPropertyDescriptor(HTMLSelectElement.prototype, 'value'); 
 
Object.defineProperty(select, 'value', { 
  get: original.get, 
  set(val) { 
    console.log("set value: " + val) 
    var old = this.value 
    var res = original.set.call(this, val) 
    if (old != val) this.dispatchEvent(new Event('change')) 
    return res 
  } 
}) 
 
document.querySelector("button").addEventListener('click', function (e) { 
  console.log("Button clicked: enter") 
  document.querySelector("select").value = 2 
  console.log("Button clicked: leave") 
})
<select> 
  <option value=1 selected>Option 1</option> 
  <option value=2>Option 2</option> 
  <option value=3>Option 3</option> 
</select> 
 
<button>#2</button>

Это же решение можно применить для всех select'ов:

document.querySelector("select").addEventListener('change', function (e) { 
  console.log("Changed to: " + e.target.value) 
}) 
 
var original = Object.getOwnPropertyDescriptor(HTMLSelectElement.prototype, 'value'); 
 
Object.defineProperty(HTMLSelectElement.prototype, 'value', { 
  get: original.get, 
  set(val) { 
    console.log("set value: " + val) 
    var old = this.value 
    var res = original.set.call(this, val) 
    if (old != val) this.dispatchEvent(new Event('change')) 
    return res 
  } 
}) 
 
document.querySelector("button").addEventListener('click', function (e) { 
  console.log("Button clicked: enter") 
  document.querySelector("select").value = 2 
  console.log("Button clicked: leave") 
})
<select> 
  <option value=1 selected>Option 1</option> 
  <option value=2>Option 2</option> 
  <option value=3>Option 3</option> 
</select> 
 
<button>#2</button>

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

READ ALSO
Добавить пустое поле в DropDownList

Добавить пустое поле в DropDownList

Подскажите, возможно ли добавить пустое значение в уже сформированный список и чтобы оно выводилось первым на вью?

221
Подключение к postgresql js

Подключение к postgresql js

Как подключиться к postgresql c помощью js ?

208
Всплывающее предупреждение для Recapcha

Всплывающее предупреждение для Recapcha

Добрый деньСоздана Ajax форма записи на консультацию

201