VUE how to evoke keydown method - javascript

i would like to create method which will run when any keyboard key will down, and then i will check which key was down.
What is important, it should run even if focus will be out of any inputs and so on.
I've created evoking when focus is in 'input' using:
#keydown.native="keymonitor"
but i would like to evoke 'keymonitor' method when there is no focus on input, but coursor is anywhere on website and focus is anywhere.
How to do it? if a add
#keydown.native="keymonitor"
to general div or body, it doesn't work.
Thanks for help.

You can use the normal EventTarget#addEventListener on window as follows:
new Vue({
el:"#app",
created() {
window.addEventListener('keydown', e => {
console.log(e.keyCode);
});
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app"></div>

You can use the keydown event.
Here is an example within a Vue component:
<template>
<div>
{{ lastKeyPressed }}
</div>
</template>
<script>
export default {
data() {
return {
lastKeyPressed: null,
}
},
created() {
window.addEventListener("keydown", (e) => {
this.lastKeyPressed = e.keyCode;
console.log(e.keyCode);
});
}
};
</script>
Be sure to use an arrow function on the addEventListener callback to ensure that this keyword works correctly, see this for other examples.

Related

keydown firing multiple times when keydown (reactjs)

I am adding an event listener and checking if its level 1, but when I press the space key once, it fires 50times or more. Please help
document.addEventListener("keyup", function(e) {
if(level === 1){
if(e.code === "Space") {
console.log('space press');
click1();
}
}
});
Since this is tagged with React, given the code you have here and the issue you describe, it is almost certain that you are binding an event listener every render. Which means you are ending up with way more listeners than you want. What you need to do is use React when you are using React.
For example below, we have an input that logs on any keypress, and we also manually create an event listener. At first, when you type, you will get one log for each. However, once you click the button (triggering a rerender), you will start getting multiple "manual" events, but still the single "react" event:
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = { count: props.count };
}
inc() {
this.setState(prev => ({count: prev.count+1}));
}
render() {
document.addEventListener("keyup", function(e) {
console.log('manual space press');
});
return <div onKeyUp={(e) => {
console.log('React: space press');
}}>
<button onClick={() => this.inc()}>{this.state.count}</button>
<input />
</div>
}
}
ReactDOM.render(<Hello count={0}/>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'></div>
This is something called Event Bubblingwhich basically means that the event gets fired once on each parent element until it reached HTML.
you can learn about it here: https://dev.to/eladtzemach/event-capturing-and-bubbling-in-react-2ffg#:~:text=Event%20Bubbling%20and%20Capturing%20in%20React&text=Bubbling%20is%20as%20straightforward%20as,our%20example%20in%20the%20beginning.
you are able to prevent the default behavior but it's generally a good practice to leave it as is if you don't have a specific use for disabling it.
from the code snippet, I don't see why is this tagged with react but another reason for your problem is that you may be putting this code inside of your render() function or inside of any react life cycle function which is causing this snippet to run with each rerender leaving you with a punch of unwanted listeners which is not only functionality you don't want but also something that will slow down you app
overtime ie. until the user refresh the page.
useEffect(() => {
const handleEscape = (event) => {
if (event.keyCode === 27) {
console.log('Hello')
}
};
window.addEventListener('keydown', handleEscape);
return () => {
window.removeEventListener('keydown', handleEscape);
};
}, []);

vue.js keyup, keydown events one character behind

I'm using the keydown/keyup events which call a javascript function that prints the value of input box to the console (and also the value of the currentTarget field of the event), and I am noticing it is a character late. For example, if I type hello into the input box, I only see hell in the console, until I press another key and then I see hello, even though by this point I've typed hello1. Why is this? And is there anyway around it?
Here's the HTML:
<input type="text" class="form__field" v-model="keywords" v-on:keyup.enter="queryForKeywords" v-on:keydown="queryForKeywords">
And the JS:
queryForKeywords: function(event) {
var self = this;
if (this.keywords.length > 2) {
console.log("keywords value: " + this.keywords);
console.log("event value: " + event.currentTarget.value);
}
Because you are depending on the input's v-model to update the keywords property, the value won't update until the Vue component has re-rendered.
You can access the updated value of keywords in a callback passed to this.$nextTick like in this example:
new Vue({
el: '#app',
data() {
return { keywords: '' }
},
methods: {
queryForKeywords: function(event) {
this.$nextTick(() => {
if (this.keywords.length > 2) {
console.log("keywords value: " + this.keywords);
}
});
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.min.js"></script>
<div id="app">
<input type="text" class="form__field" v-model="keywords" v-on:keyup.enter="queryForKeywords" v-on:keydown="queryForKeywords">
</div>
The real problem doesn't has to do with vue.js at all
The problem hides behind the keydown event!
So when the event fires, the input value is NOT updated yet. Fiddle example
MDN - keydown event
In general, keydown it is used for informing you which key is pressed. And you can access it like this:
document.addEventListener('keydown', logKey);
function logKey(e) {
console.log(e.key)
}
As solution, you can use the keyup event: Fiddle
My recommendation is to use a custom v-model using :value and the #input event.
<input type="text" :value="keywords" #input="queryForKeywords">
And the script:
data: {
keywords: ''
},
methods: {
queryForKeywords(event) {
const value = event.target.value
this.keywords = value
if (value.length > 2) {
console.log("keywords value: " + this.keywords);
}
}
}
See it in action
The currently accepted answer is for an old version of vue, in the latest versions should be used #input instead of keypress or keyup.

Enter with #keyup event not working in Vue

I am trying to call method on pressing enter key but it's not working. Code is as below.
<template>
<div>
<button #click="callEvent" #keyup.enter="callEvent"> Click </button>
</div>
</template>
<script>
export default{
methods:{
callEvent(){
console.log("Event called");
}
}
}
</script>
The click event already triggers with the ENTER key (it also triggers with Space in some browsers, like Chrome for desktop). So, your code only needs a #click="callEvent" and everything works well since the focus is already on the button:
var app = new Vue({
el: "#app",
methods: {
callEvent() {
console.log("Event called");
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<div id="app">
<button #click="callEvent">Enter</button>
</div>
If you want that any ENTER triggers the button even if it isn't with focus, you should bind the event to the window object, which can be made inside the mounted handler:
var app = new Vue({
el: "#app",
methods: {
callEvent() {
console.log("Event called");
}
},
mounted() {
window.addEventListener('keyup', function(event) {
if (event.keyCode === 13) {
app.callEvent();
}
});
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<div id="app">
<button>Enter</button>
</div>
Remember that if you're using Single File Components, the instance is exposed by the this keyword, which can be used to call component methods inside the desired handler:
export default {
methods: {
callEvent() {
console.log('Event called')
}
},
mounted() {
window.addEventListener('keyup', event => {
if (event.keyCode === 13) {
this.callEvent()
}
})
}
}
Buttons don't have keyup event on them. Even when you have focus on the button, and hit enter, it will be considered a click event, instead of keyup.enter.
Try binding the event to an input and it'd work.
Alternatively, you could use jQuery (or Plain JS) to bind for keydown event on the body element, and trigger the Vue method by calling app.callEvent().
var app = new Vue({
el: "#app",
methods: {
callEvent() {
console.log("Event called");
}
},
mounted() {
var self = this;
window.addEventListener('keyup', function(event) {
if (event.keyCode === 13) {
self.callEvent();
}
});
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<div id="app">
<template>
<div>
<button #click="callEvent"> Click </button>
</div>
<input type="text" #keyup.enter="callEvent" />
</template>
</div>
Updated to use mounted instead of relying on jQuery - as per Erick Petrucelli's answer as it allows referring to the Vue component without the global variable.
I experienced inconsistent results when using native JS with window.addEventListener. VueJS natively supports modifying behavior for keyboard events https://v2.vuejs.org/v2/guide/events.html#Key-Modifiers
This also worked a lot better in my case due to needing separate behavior for the tab key.
Your input can look like this with custom modifiers on each key up|down
<input
type="text"
class="form-control"
placeholder="Start typing to search..."
v-model="search_text"
#focus="searchFocus"
#blur="searchFocusOut"
v-on:keyup.enter="nextItem"
v-on:keyup.arrow-down="nextItem"
v-on:keyup.arrow-up="nextItem"
v-on:keydown.tab="nextItem"
>
Then inside NextItem you can reference the event, and get each key.. or write a separate function for each key modifier.
#keyup.enter="callEvent"
change to
#keypress.enter.prevent="callEvent"
<template>
<div>
<button #click="callEvent" #keypress.enter.prevent="callEvent"> Click </button>
</div>
</template>
Ref: https://github.com/vuejs/vue/issues/5171

Using spread operator with event listeners in VueJS 2 and JSX

Here is a simple version for my problem:
render (h) {
let events = {onClick: handleClick}
return (<div {...events}></div>)
}
onClickevent was not add to the div element, and spread operator works well with class and style attribute but not any of the event listener bindings (starts with on or nativeOn). Can somebody explain why and offer me a solution that I can bind arbitrary amount of event on a element?
it should be like this:
render (h) {
const hi = function () {
console.log('hello')
}
return (
<div { ...{ on: {click: hi} } } > click me! </div>
)
}
Here the link to babel-plugin-transform-vue-jsx issues

Simulate click event on react element

The bounty expires in 7 days. Answers to this question are eligible for a +50 reputation bounty.
ajaykools wants to reward an existing answer:
Worth bounty, only way simulate clicks on dynamic elements like svg, g, circle, etc which are generated on page load.
I'm trying to simulate a .click() event on a React element but I can't figure out why it is not working (It's not reacting when I'm firing the event).
I would like to post a Facebook comment using only JavaScript but I'm stuck at the first step (do a .click() on div[class="UFIInputContainer"] element).
My code is:
document.querySelector('div[class="UFIInputContainer"]').click();
And here's the URL where I'm trying to do it: https://www.facebook.com/plugins/feedback.php...
P.S. I'm not experienced with React and I don't know really if this is technically possible. It's possible?
EDIT: I'm trying to do this from Chrome DevTools Console.
React tracks the mousedown and mouseup events for detecting mouse clicks, instead of the click event like most everything else. So instead of calling the click method directly or dispatching the click event, you have to dispatch the down and up events. For good measure I'm also sending the click event but I think that's unnecessary for React:
const mouseClickEvents = ['mousedown', 'click', 'mouseup'];
function simulateMouseClick(element){
mouseClickEvents.forEach(mouseEventType =>
element.dispatchEvent(
new MouseEvent(mouseEventType, {
view: window,
bubbles: true,
cancelable: true,
buttons: 1
})
)
);
}
var element = document.querySelector('div[class="UFIInputContainer"]');
simulateMouseClick(element);
This answer was inspired by Selenium Webdriver code.
With react 16.8 I would do it like this :
const Example = () => {
const inputRef = React.useRef(null)
return (
<div ref={inputRef} onClick={()=> console.log('clicked')}>
hello
</div>
)
}
And simply call
inputRef.current.click()
Use refs to get the element in the callback function and trigger a click using click() function.
class Example extends React.Component{
simulateClick(e) {
e.click()
}
render(){
return <div className="UFIInputContainer"
ref={this.simulateClick} onClick={()=> console.log('clicked')}>
hello
</div>
}
}
ReactDOM.render(<Example/>, document.getElementById('app'))
<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>
<div id="app"></div>
If you don't define a class in your component, and instead you only declare:
function App() { ... }
In this case you only need to set up the useRef hook and use it to point/refer to any html element and then use the reference to trigger regular dom-events.
import React, { useRef } from 'react';
function App() {
const inputNameRef = useRef()
const buttonNameRef = useRef()
function handleKeyDown(event) {
// This function runs when typing within the input text,
// but will advance as desired only when Enter is pressed
if (event.key === 'Enter') {
// Here's exactly how you reference the button and trigger click() event,
// using ref "buttonNameRef", even manipulate innerHTML attribute
// (see the use of "current" property)
buttonNameRef.current.click()
buttonNameRef.current.innerHTML = ">>> I was forced to click!!"
}
}
function handleButtonClick() {
console.log('button click event triggered')
}
return (
<div>
<input ref={inputNameRef} type="text" onKeyDown={handleKeyDown} autoFocus />
<button ref={buttonNameRef} onClick={handleButtonClick}>
Click me</button>
</div>
)
}
export default App;
A slight adjustment to #carlin.scott's great answer which simulates a mousedown, mouseup and click, just as happens during a real mouse click (otherwise React doesn't detect it).
This answer adds a slight pause between the mousedown and mouseup events for extra realism, and puts the events in the correct order (click fires last). The pause makes it asynchronous, which may be undesirable (hence why I didn't just suggest an edit to #carlin.scott's answer).
async function simulateMouseClick(el) {
let opts = {view: window, bubbles: true, cancelable: true, buttons: 1};
el.dispatchEvent(new MouseEvent("mousedown", opts));
await new Promise(r => setTimeout(r, 50));
el.dispatchEvent(new MouseEvent("mouseup", opts));
el.dispatchEvent(new MouseEvent("click", opts));
}
Usage example:
let btn = document.querySelector("div[aria-label=start]");
await simulateMouseClick(btn);
console.log("The button has been clicked.");
Note that it may require page focus to work, so executing in console might not work unless you open the Rendering tab of Chrome DevTools and check the box to "emulate page focus while DevTools is open".
Inspired from previous solution and using some javascript code injection it is also possibile to first inject React into the page, and then to fire a click event on that page elements.
let injc=(src,cbk) => { let script = document.createElement('script');script.src = src;document.getElementsByTagName('head')[0].appendChild(script);script.onload=()=>cbk() }
injc("https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js",() => injc("https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js",() => {
class ReactInjected extends React.Component{
simulateClick(e) {
e.click()
}
render(){
return <div className="UFIInputContainer"
ref={this.simulateClick} onClick={()=> console.log('click injection')}>
hello
</div>
}
}
ReactDOM.render(<ReactInjected/>, document.getElementById('app'))
} ))
<div id="app"></div>
Kind of a dirty hack, but this one works well for me whereas previous suggestions from this post have failed. You'd have to find the element that has the onClick defined on it in the source code (I had to run the website on mobile mode for that). That element would have a __reactEventHandlerXXXXXXX prop allowing you to access the react events.
let elem = document.querySelector('YOUR SELECTOR');
//Grab mouseEvent by firing "click" which wouldn't work, but will give the event
let event;
likeBtn.onclick = e => {
event = Object.assign({}, e);
event.isTrusted = true; //This is key - React will terminate the event if !isTrusted
};
elem.click();
setTimeout(() => {
for (key in elem) {
if (key.startsWith("__reactEventHandlers")) {
elem[key].onClick(event);
}
}
}, 1000);
Using React useRef Hooks you can trigger a click event on any button like this:
export default const () => {
// Defining the ref constant variable
const inputRef = React.useRef(null);
// example use
const keyboardEvent = () => {
inputRef.current.handleClick(); //Trigger click
}
// registering the ref
return (
<div ref={inputRef} onClick={()=> console.log('clicked')}>
hello
</div>
)
}
This answer was inspired by carlin.scott code.
However, it works only with focusin event in my case.
const element = document.querySelector('element')
const events = ['mousedown', 'focusin']
events.forEach(eventType =>
element.dispatchEvent(
new MouseEvent(eventType, { bubbles: true })
)
)

Categories

Resources