Detecting keyboard button and play sound in vue.js - javascript

The code below actually plays sounds on each button click with vue.js.
How is it possible to detect keyboard press and play sound when DOM is ready and not when the buttons are clicked?
for example on enter play sound v-on:keyup.13="playSound('path/to/mp3')"
Vue documentation mostly explains html attributes, perhaps this is something that needs to be done in JS, i guess. I am new to Vue.js
Vue.js documentation on Event Modifiers
See the codepen.
new Vue({
el: '#app',
data: {
text: ''
},
methods: {
playSound (sound) {
if(sound) {
var audio = new Audio(sound);
audio.play();
}
}
}
});

When you press a key, the keyboard event will be fired from the active element and bubble upwards. So if you want to handle all key presses regardless of what element has focus, then you will need to register the listener manually in code on, e.g., document.
new Vue({
el: '#app',
created() {
this.onKeyDown = this.onKeyDown.bind(this);
document.addEventListener('keydown', this.onKeyDown);
},
destroyed() {
document.removeEventListener('keydown', this.onKeyDown);
},
methods: {
playSound (sound) {
if(sound) {
var audio = new Audio(sound);
audio.play();
}
},
onKeyDown(e) {
switch (e.keyCode) {
case 65: this.playSound(sound1); break; // 'a' key
case 66: this.playSound(sound2); break; // 'b' key
}
},
}
});
Codepen

Related

How to get codemirror 'change' event to fire in Grapesjs plugin

I'm building a Grapesjs plugin and have added a 'jscript' trait to a button component, which appears as a codemirror textarea. The idea is for users to be able to edit some javascript code associated with a button. I can't seem to intercept the codemirror area's change event, at least, not the proper codemirror specific version.
Happily, when I edit the codemirror area and change focus, a regular 'change' event triggers the Grapejs onEvent handler within my plugin's editor.TraitManager.addType('jcodemirror-editor', {} - good. I can then store the contents of the codemirror area into the trait.
onEvent({ elInput, component, event }) {
let code_to_run = elInput.querySelector(".CodeMirror").CodeMirror.getValue()
component.getTrait('jscript').set('value', code_to_run);
},
However if we paste or backspace or delete etc. in the codemirror area then the regular 'change' event is never issued!
So I'm trying to intercept the deeper codemirror specific 'change' event which is usually intercepted via cm.on("change", function (cm, changeObj) {} and which is triggered more reliably (unfortunately also on each keystroke). How do I wire this codemirror specific event to trigger the usual onEvent({ elInput, component, event }) {} code?
I have a workaround in place in my https://jsfiddle.net/tcab/1rh7mn5b/ but would like to know the proper way to do this.
My Plugin:
function customScriptPlugin(editor) {
const codemirrorEnabled = true // otherwise trait editor is just a plain textarea
const script = function (props) {
this.onclick = function () {
eval(props.jscript)
}
};
editor.DomComponents.addType("customScript", {
isComponent: el => el.tagName == 'BUTTON' && el.hasAttribute && el.hasAttribute("data-scriptable"),
model: {
defaults: {
traits: [
{
// type: 'text',
type: 'jcodemirror-editor', // defined below
name: 'jscript',
changeProp: true,
}
],
script,
jscript: `let res = 1 + 3; console.log('result is', res);`,
'script-props': ['jscript'],
},
},
});
editor.TraitManager.addType('jcodemirror-editor', {
createInput({ trait }) {
const el = document.createElement('div');
el.innerHTML = `
<form>
<textarea id="myjscript" name="myjscript" rows="14">
</textarea>
</form>
</div>
`
if (codemirrorEnabled) {
const textareaEl = el.querySelector('textarea');
var myCodeMirror = CodeMirror.fromTextArea(textareaEl, {
mode: "javascript",
lineWrapping: true,
});
// This is the 'more accurate' codemirror 'change' event
// which is triggered key by key. We need it cos if we paste
// or backspace or delete etc. in codemirror then the
// regular 'change' event is never issued! But how do we get
// this event to trigger the proper, usual 'onEvent' below?
// Currently cheating and doing the onEvent work here with
// this special handler.
myCodeMirror.on("change", function (cm, changeObj) { // HACK
const component = editor.getSelected()
const code_to_run = myCodeMirror.getValue()
component.getTrait('jscript').set('value', code_to_run);
console.log('onEvent hack - (myCodeMirror change event) updating jscript trait to be:', code_to_run)
})
}
return el;
},
// UI textarea & codemirror 'change' events trigger this function,
// so that we can update the component 'jscript' trait property.
onEvent({ elInput, component, event }) {
let code_to_run
if (codemirrorEnabled)
code_to_run = elInput.querySelector(".CodeMirror").CodeMirror.getValue()
else
code_to_run = elInput.querySelector('textarea').value
console.log('onEvent - updating jscript trait to be:', code_to_run)
component.getTrait('jscript').set('value', code_to_run);
}, // onEvent
// Updates the trait area UI based on what is in the component.
onUpdate({ elInput, component }) {
console.log('onUpdate - component trait jscript -> UI', component.get('jscript'))
if (codemirrorEnabled) {
const cm = elInput.querySelector(".CodeMirror").CodeMirror
cm.setValue(component.get('jscript'))
// codemirror content doesn't appear till you click on it - fix with this trick
setTimeout(function () {
cm.refresh();
}, 1);
}
else {
const textareaEl = elInput.querySelector('textarea');
textareaEl.value = component.get('jscript')
// actually is this even needed as things still update automatically without it?
// textareaEl.dispatchEvent(new CustomEvent('change'));
}
}, // onUpdate
}) // addType
editor.BlockManager.add(
'btnRegular',
{
category: 'Basic',
label: 'Regular Button',
attributes: { class: "fa fa-square-o" },
content: '<button type="button">Click Me</button>',
});
editor.BlockManager.add(
'btnScriptable',
{
category: 'Scriptable',
label: 'Scriptable Button',
attributes: { class: "fa fa-rocket" },
content: '<button type="button" data-scriptable="true">Run Script</button>',
});
}
const editor = grapesjs.init({
container: '#gjs',
fromElement: 1,
height: '100%',
storageManager: { type: 0 },
plugins: ['gjs-blocks-basic', 'customScriptPlugin']
});
According to official Grapesjs documentation on traits integrating external ui components you can trigger the onEvent event manually by calling this.onChange(ev).
So within createInput I continued to intercept the more reliable myCodeMirror.on("change", ... event and within that handler triggered the onEvent manually viz:
editor.TraitManager.addType('jcodemirror-editor', {
createInput({ trait }) {
const self = this // SOLUTION part 1
const el = document.createElement('div');
el.innerHTML = `
<form>
<textarea id="myjscript" name="myjscript" rows="14">
</textarea>
</form>
</div>
`
if (codemirrorEnabled) {
const textareaEl = el.querySelector('textarea');
var myCodeMirror = CodeMirror.fromTextArea(textareaEl, {
mode: "javascript",
lineWrapping: true,
});
myCodeMirror.on("change", function (cm, changeObj) {
self.onChange(changeObj) // SOLUTION part 2
})
}
return el;
},

vuejs mobile router-link prevented by v-on:mouseover

have this for list:
<li
class="projects-item"
v-for="project in filteredProjects"
:key="project.id"
v-on:mouseover="displayHoverInfo($event, project)"
v-on:mouseleave="hover = false"
>
<router-link v-bind:to="'/project/' + project.slug">
and js:
displayHoverInfo(event, project) {
this.hover = true;
this.hoveredProject = project;
console.log(event);
}
on desktop works all fine, on mobile (on tap) only the v-on:mouseover/v-on:mouseleave events are being triggered.
I stole the answer here: https://forum.vuejs.org/t/how-to-disable-mouseover-on-mobile/37335
You can disable the mouseover and mouseleave on mobile. Going further you can detect if it's actually a mobile/tablet using the user agent.
<li
class="projects-item"
v-for="project in filteredProjects"
:key="project.id"
v-on:mouseover="isMobile ? null : displayHoverInfo($event, project)"
v-on:mouseleave="isMobile ? null : hover = false"
>
new Vue({
el: "#app",
data: {
isMobile: false,
hover: false
},
methods: {
mq() {
this.isMobile = window.matchMedia('(max-width: 400px)').matches;
},
displayHoverInfo($event, project) {
// your method
}
},
created () {
// get initial val
this.mq()
// listen to resize
window.addEventListener('resize', this.mq)
},
beforeDestroy() {
window.removeEventListener('resize', this.mq)
}
})

How i can add click handler function to dynamically link in Vue?

Here in Vue component I receive dynamically message from server:
module.exports = {
data() {
return: { windowText: '' }
},
methods: {
showCancelEntrieWindow(){
this.$http.post('/page', {'number' : '123'})
.then(response => {
responseText = response.data.message;
this.windowText = responseText.replace(
new RegExp("class='action'", 'g'),
'v-on:click="myclick"'
);
});
},
myclick(){
console.log('clicked!');
}
}
};
Message have a link with class="action".
As example:
response.data.message = 'Some text... <a class="action" href="/test">test</a>';
In template:
<div v-html="windowText"></div>
How I can add some click handler function to this link?
I am trying to edit response.data.message with replace function like this:
this.windowText = responseText.replace(
new RegExp("class='action'", 'g'),
'v-on:click.stop="myclick"'
);
But it does not work.
Please help me.
And ofcourse, I can't edit response.data.message.
v-html will not compile the template, so replacing the class with the Vue directive will not do anything.
You can, however, use a native event listener.
new Vue({
el: "#app",
data:{
windowText: null,
someValueSetOnClick: null
},
methods:{
onHtmlClick(event){
// Check to make sure this is from our v-html because
// we don't want to handle clicks from other things in
// the Vue
if (!event.target.classList.contains("action"))
return;
event.stopPropagation();
event.preventDefault();
this.someValueSetOnClick = "Clicked";
}
},
mounted(){
this.windowText = 'Some text... <a class="action" href="/test">test</a>'
// Add a native event listener to the Vue element.
this.$el.addEventListener("click", this.onHtmlClick)
}
})
Example.

How can i detect multiple key press (up/down) on Vue.js

I am trying to implement common chat app on Vue.js.
window.onload = function () {
new Vue({
el: '#vue-chat',
data: {
body: ''
},
methods: {
fooMethod: function () {
alert('foo');
},
barMethod: function () {
alert('bar');
}
}
})
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.3/vue.js"></script>
<div id="vue-chat">
<ul class="comments">
<li></li>
</ul>
<input type="text" v-model="body" #keyup.enter="fooMethod">
</div>
and i want to call barMethod when users press enter key and shift key at the same time.
I read docs however I could not find the way.
Thank you for reading!
With the shift key and other modifier keys you can see if they were pressed through the event object.
I'd use a single method with #keyup.enter and then decide to which method to call based on the event's shiftKey value.
new Vue({
el: '#app',
data: {
message: 'Hi',
},
methods: {
action(event) {
if (event.shiftKey) {
this.shiftKeyPressed()
} else {
this.shiftKeyNotPressed()
}
},
shiftKeyPressed() {
console.log('Shift key was pressed.')
},
shiftKeyNotPressed() {
console.log('Shift key was NOT pressed.')
},
}
})
Here's a quick demo: https://jsfiddle.net/bj75cyd3/
There is no trivial way to do what you want.
You can't reach your goal through modifiers; you have to drop the .enter modifier and deal with the keyup event, as well as the keydown event.
<input type="text" v-model="body" #keyup="keyUp" #keydown="keyDown">
There are a short answer and a long answer suggesting how to track multiple keys pressed at once in JavaScript.
Based on the answers linked above, we can build the basis of our Vue solution:
data: {
shiftPressed: false
},
methods: {
keyDown: function (event) {
if (event.keyCode === 16) {
this.shiftPressed = true
}
},
keyUp: function(event) {
if (event.keyCode === 16) {
this.shiftPressed = false
}
if (this.shiftPressed && (event.keyCode === 13)) {
this.shiftPressed = false // avoid double trigger
this.fooMethod()
}
}
}

Listen to custom event in Vue.js

Vue.js works great with browser events such as click or mousedown. But not work at all with custom events. Here is the code:
HTML:
<div id="app" style="display: none" v-show="true">
<div v-el:ping v-on:ping="ping">
<div>
<button v-on:click="click">Click</button>
</div>
</div>
</div>
JavaScript:
new Vue({
el: '#app',
data: {
},
methods: {
ping: function (event) {
console.log('Vue ping', event);
alert('Vue ping');
},
click: function (event) {
jQuery(event.target).trigger('ping');
}
},
ready: function () {
console.log(this.$els);
jQuery(this.$els.ping).on('ping', function (event) {
console.log('jQuery ping', event);
alert('jQuery ping');
});
}
});
I expect alert with Vue ping and jQuery ping. But only the later pops up.
CodePen
Vue has its own internal system for custom events, which you should use instead of jQuery / native DOM events:
click: function (event) {
// jQuery(event.target).trigger('ping');
this.$dispatch('ping', event.target) // send event up the parent chain, optionally send along a reference to the element.
// or:
this.$emit('ping') // trigger event on the current instance
}
Edit: $dispatch is for parent-child communication, You seem to want to trigger a custom event from within the same comonent. In that case, you could instead simply call a method.
If you still want to listen to a custom event inside the same component, you:
want to use $emit
cannot use v-on:custom-event-name in the template (that's only to be used on components). Rather, add the event method to the events::
events: {
ping: function() {....}
}
Here it is in vanilla JS:
HTML:
<div id="app">
<div v-el:ping>
<div>
<button v-on:click="click">Click</button>
</div>
</div>
</div>
JS:
(function() {
new Vue({
el: '#app',
data: {
event: null
},
methods: {
ping: function(event) {
alert('Vue ping');
},
click: function(event) {
this.$els.ping.dispatchEvent(this.event);
}
},
ready: function() {
this.event = document.createEvent("HTMLEvents");
this.event.initEvent("ping", true, true);
this.$els.ping.addEventListener('ping', this.ping);
}
});
})();
pen: http://codepen.io/anon/pen/wGdvaV?editors=1010#0
You should avoid to mix a dom events and vue-components related ones because it's a different layers of abstraction.
Anyway, if you still want to do that, I think you need to cache this.el inside a vue-component instance or take it via computed-property like this
{
computed : {
jqueryEl(){ return $(this.el) }
}
}
And then trigger a custom jQuery events by this.jqueryEl.trigger('ping').
Sure to properly take care of keep the element's bindings up to date!
For example you can bind jQuery events dynamically (and also unbind on component destroy!) like this:
ready : function(){
jQuery('body').on('ping.namespace', '[data-jquery="ping"]', function(){ ... })
},
destroy : function(){
jQuery('body').off('ping.namespace')
}
And don't forget to add attribute [data-jquery="ping"] to an element which you would like to response a ping event.
Hope this information helps you to achieve the expected result.

Categories