简体   繁体   中英

Javascript binding Higher-order function with input onchange not working

My goal is to call a function with a string argument, calling function will return a function which will receive the event object from html input object, and I want to use the string argument in the second function.

 const person = { name:'', age:'' }; const regForm = (field) => { console.log('field : ', field); return event => { person[field]=event.target.value; document.getElementById("demo").innerHTML = JSON.stringify(person); } };
 <input onchange="regForm('name')"/> <input onchange="regForm('age')"/> <p id="demo"></p>

The issue is your onchange attribute currently has a string-based value and it is effectively eval d when the change event is fired. As @PeterSeliger comments, regForm simply returns a function.

regForm could return anything, and so the default change handler makes no assumptions about your answer. You maybe be expecting that the returned function would be called with the event, but instead the default handler simply discards the value.

One solution would be to use JavaScript's onchange property , instead of HTML's onchange attribute -

 const person = { name:'', age:'' } const regForm = field => event => person[field] = event.target.value const form = document.forms.sample form.name.onchange = regForm("name") form.age.onchange = regForm("age") form.onsubmit = event => { event.preventDefault() console.log("Submitted", JSON.stringify(person)) }
 <form id="sample"> <input name="name" placeholder="enter your name" /> <input name="age" placeholder="enter your age" /> <input type="submit" /> </form>


And since you are familiar with event delegation, ie event.target , you can remove additional duplication. Looks like regForm just kinda disappeared! -

 const data = {} const form = document.forms.sample form.onchange = event => data[event.target.name] = event.target.value form.onsubmit = event => { event.preventDefault() console.log("Submitted", JSON.stringify(data)) }
 <form id="sample"> <input name="name" placeholder="enter your name"><br> <input name="age" placeholder="enter your age"><br> <input name="foo" placeholder="enter your foo"><br> <input name="bar" placeholder="enter your bar"><br> <input type="submit" /> </form>

Output

Submitted {"name":"1","age":"2","foo":"3","bar":"4"}

Functions that take other functions as input and/or return other functions as output are called higher-order functions . There are a variety of terminologies and techniques for dealing with them. For related reading, see What do multiple arrow functions mean ?

 const preventDefault = f => event => ( event.preventDefault() , f(event) ) const logKeypress = event => console.log(event.which) document .querySelector('input[name=foo]') .addEventListener('keydown', preventDefault(logKeypress))
 <input name="foo" placeholder="type here to see ascii codes" size="50">

In oder to come close to what the OP might have wished to achieve, one should break down the code into the state-change handling task(, maybe a render task) and the listener-initializing task ...

 const person = { name:'', age:'' }; function renderPersonStateChange(personReference, key, value) { personReference[key] = value; document.body.querySelector('#demo').textContent = JSON.stringify(personReference); } function handleTextInputStateChange(evt) { const elm = evt.currentTarget; const key = elm.name; renderPersonStateChange(person, key, elm.value); } // initialize event listeners document.body.querySelectorAll('input').forEach(elm => elm.addEventListener('change', handleTextInputStateChange, false) );
 <input name='name' placeholder="enter person's name"/> <input name='age' placeholder="enter person's age"/> <p> <code> <pre id="demo"> </pre> </code> </p>

If one wishes to bind the reference that's state should be changed, the above code then slightly alters (only for the change-handler and the event-initialization parts) towards ...

 const person = { name:'', age:'' }; function renderPersonStateChange(personReference, key, value) { personReference[key] = value; document.body.querySelector('#demo').textContent = JSON.stringify(personReference); } function handleStateChangeForBoundPerson(evt) { const personReference = this; const elm = evt.currentTarget; const key = elm.name; renderPersonStateChange(personReference, key, elm.value); } // initialize event listeners document.body.querySelectorAll('input').forEach(elm => elm.addEventListener('change', handleStateChangeForBoundPerson.bind(person), false) );
 <input name='name' placeholder="enter person's name"/> <input name='age' placeholder="enter person's age"/> <p> <code> <pre id="demo"> </pre> </code> </p>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM