I am building a headless crawler running JavaFX Webkit, it definitely is not as powerful as chrome's v8.
However I've run into problems lately, wherein I am trying to input a value to react rendered input fields.
Here's what I have done till now and failed.[ Note: I don't have control over the source / React code. Since I am trying to crawl a destination site ]
$('input.r_input').val("2");
document.querySelector("input.r_input").value = "2";
Creating a manual event like :
event = new Event( event, {target: obj, bubbles: true} );
event.simulated = true;
return obj ? obj.dispatchEvent(event) : false;
and triggering an input
event.
None of the above works.
I am adding parts of react code from the JS file on the website if it may help to add some more context.
Create:
t.prototype.createInputProps = function(e) {
return {
disabled: this.props.submitting || e > this.focusIndex,
className: "r_input",
type: "tel",
name: "tan-" + e,
maxLength: 1,
pattern: "[\\d]*",
tabIndex: 0,
placeholder: "·",
autoComplete: "off"
}
}
Render :
t.prototype.render = function() {
var e = this.props,
t = e.meta,
n = t.touched,
r = t.error,
o = (e.input.value, sa()("r_input", {
"has-error": r && n
}));
return us("article", {
className: o
}, void 0, us("div", {
className: "r_inputs"
}, void 0, ro.a.createElement("input", as({
onPaste: this.handleOnPaste,
ref: this.addInputToList,
onKeyUp: this.handleKeyUp,
value: this.getValue(0)
}, this.createInputProps(0))), ro.a.createElement("input", as({
ref: this.addInputToList,
onKeyUp: this.handleKeyUp,
value: this.getValue(1)
}, this.createInputProps(1))), ro.a.createElement("input", as({
ref: this.addInputToList,
onKeyUp: this.handleKeyUp,
value: this.getValue(2)
}, this.createInputProps(2))), ro.a.createElement("input", as({
ref: this.addInputToList,
onKeyUp: this.handleKeyUp,
value: this.getValue(3)
}, this.createInputProps(3))), ro.a.createElement("input", as({
ref: this.addInputToList,
onKeyUp: this.handleKeyUp,
value: this.getValue(4)
}, this.createInputProps(4))), ro.a.createElement("input", as({
ref: this.addInputToList,
onKeyUp: this.handleKeyUp,
value: this.getValue(5)
}, this.createInputProps(5)))), n && r && us(is.a, {}, void 0, r))
}
Not sure If I need to add handleKeyUp
, but that contains some validation code.
Any help will be appreciated.
The React AutoComplete supports the autofill behavior with the help of autofill property. Whenever you change the input value and press the down key, the AutoComplete will autocomplete your data by matching the typed character.
In your case, it looks like you can fire a keyup
event on the element after changing the value. You may also have to wait for the element to appear in the DOM, based on a React state change. Try this
setTimeout(function() {
var event = new Event('keyup', { bubbles: true });
$('input.r_input').val("2")[0].dispatchEvent(event);
}, 3000);
It will of course be hard to provide something definitive without access to the page in question.
Note: I came up with the voodoo to dispatch a keyup
event via this thread which says the React onChange
handler listens for input
events and by looking at the code you provided which calls onKeyUp
. My sample code uses onChange
handler in React & dispatches input
events as prescribed in the linked thread.
Also Note: I failed to get this working with jQuery's trigger
method, maybe you can figure out what I was missing there, but event dispatching with native Javascript is working in my snippet below.
Read on for details!
There is no single answer for your question, because how a given React component reads in values from input
fields can vary, so you'll need to look at each React implementation you are trying to manipulate. I'd hazard a guess that onChange
is the most common way for React to capture input in a text field. The React onChange
handler listens for input
events.
It's also possible React's state is updating to cause the elements you are looking for to be added to the DOM after jQuery's .ready()
callback fires. This would mean your selectors aren't finding anything because the elements they are looking for are not present in the DOM when they are looking. (This is why I added the setTimeout
in my suggested solution).
Here I illustrate the issue of elements appearing in the DOM after a React state change, and a working example where jQuery is used to set the value of an input
element managed by React. (Make sure to look at the console output when you run it to get an idea what's happening), you should see this in the HTML when it's all finished
React thinks the value is 50
jQuery(function() {
console.log('jquery loaded');
function searchForNodeOfInterest() {
console.log('jQuery searching for nodes of interest');
if(jQuery('#interesting-node').length === 0) {
console.log('jQuery did not find the selector of interest');
} else {
console.log('jQuery found the selector of interest, setting input value...');
var event = new Event('input', { bubbles: true });
jQuery('#interesting-node').val(50)[0].dispatchEvent(event);
}
}
searchForNodeOfInterest();
setTimeout(searchForNodeOfInterest, 4000);
console.log('jQuery - taking a nap, I will search again soon...');
});
var App = React.createClass({
getInitialState: function() {
return {
hidden: true
};
},
componentDidMount: function() {
console.log('react mounted');
},
// Imagine something happens inside react (AJAX response comes in for example),
// causing the nodes of interest to be rendered in DOM
componentWillMount: function() {
var that = this;
setTimeout(function() {
console.log('react updating the state...');
that.setState({
hidden: false,
value: null
})
}, 2000);
},
handleInput: function(e) {
console.log('react handling input...', e.target.value);
this.setState({
value: e.target.value
})
},
render: function() {
return this.state.hidden ? null : <form>React Powered Input: <input type="text" id="interesting-node" onChange={this.handleInput}/><p>React thinks the value is {this.state.value}</p></form>
}
});
ReactDOM.render(<App />, document.getElementById('react'));
<body>
<div id="react"></div>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
A more reliable technique than setTimeout
for some random amount of time is a wrapper that periodically checks for the presence of a selector, like this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With