8. Interactivity 2. `Keydown` event handling. Active key state

apayrus

Rustam Apay

Posted on August 18, 2022

8. Interactivity 2. `Keydown` event handling. Active key state

Keydown event handling

In the file Keyboard.js add to mounted() an event listener on keydown:

    mounted() {
        this.getKeyboardData(this.currentLang)

        /* add: */
        window.addEventListener('keydown', event => {
            e.preventDefault()
            console.log(event)
        })
    },
Enter fullscreen mode Exit fullscreen mode

Save the file.

Open Chrome dev tools tab console. Click with mouse on the app to make the window active to catch events from it. Press random keys on the (physical) keyboard. Events will appear in a console.

Image description

Experiment with different keys and see results. Expand KeyboardEvent and look at its properties. We need only 3 of them: code, key, and shiftKey.

Close console (Dev tools).

Diffs in code 8.1

activeKey state

In Keyboard.js data() add a new state activeKey. It will be filled with { code, key, shiftKey } from the event. Add activeKey to template to see how it will be changed.

Keyboard.js

import Key from './Key.js'

const Keyboard = {
    template: `
    <div>activeKey: {{activeKey}}</div>
    <div class="keyboard">
        <div
            v-for="(row, index) in keyboardData"
            :class="['row', 'row-'+(index+1)]"
        >
            <vue-key
                v-for="keyContent in row"
                :keyContent="keyContent"
            />
        </div>
    </div>`,
    components: {
        'vue-key': Key
    },
    data() {
        return {
            keyboardData: [],
            /* add: */
            activeKey: { code: '' }
        }
    },
    props: {
        currentLang: String
    },
    watch: {
        currentLang: function (currentLang) {
            this.getKeyboardData(currentLang)
        }
    },
    mounted() {
        this.getKeyboardData(this.currentLang)

        window.addEventListener('keydown', event => {
            event.preventDefault()
            /* add: (read particular props of event) */
            const { code, key, shiftKey } = event
            /* write event parts to the state: */
            this.activeKey = { code, key, shiftKey }
        })
    },

    methods: {
        async getKeyboardData(lang) {
            const { default: keyboardData } = await import(
                `../keyboardData/${lang}.js`
            )
            this.keyboardData = keyboardData
        }
    }
}

export default Keyboard
Enter fullscreen mode Exit fullscreen mode

Press q, w in all lang layouts (en, ru, ar).

Result:

Image description

Operational System (OS) language

You see, that the same events happen with any currentLang. That's because our web app state is not connected with OS language (for keyboard). And there is no technical ability to do this.

If a user switches a language in OS (alt+shift, ctrl+shift), an event property key will be different, but our app wont know what language is set in OS.

Anyway code is always the same. That's why we made it the required identifier in the data model.

Active key styling

In Keyboard.js pass state activeKey as a prop to Key. And warn the Key about the new prop.

Keyboard.js template:

<vue-key ... :activeKey="activeKey" />
Enter fullscreen mode Exit fullscreen mode

Key.js props:

props: {
    ...
    activeKey: Object,
}
Enter fullscreen mode Exit fullscreen mode

Now we can use activeKey inside the Key component to apply conditional styling to one of the keys (the active one).

Key.js template:

replace

<div class="key">
    <div class="main">{{main}}</div>
    <div class="shifted">{{shifted}}</div>
</div>
Enter fullscreen mode Exit fullscreen mode

with:

<div
    :class="[
            'key', 
            {active: activeKey.code === keyContent.code}
            ]"
>
    <div class="main">{{main}}</div>
    <div class="shifted">{{shifted}}</div>
</div>
Enter fullscreen mode Exit fullscreen mode

Now :class is dynamic (calculated, variable).

Style key will be applied to a button (key) in any case.

Style active will be applied only if key code is the same as the code of the activeKey. If you remember, we added .key.active style to styles.css in chapter 2.

Result:

Image description

It works with any language, and doesn't depend on a system language for the keyboard.

Fade active key after a while

There is a problem. If we press a button, and then don't press anything, activeKey stays forever. But we want it to fade after a while.

In Keyboard.js in addEventListener:

after

this.activeKey = { code, key, shiftKey }
Enter fullscreen mode Exit fullscreen mode

add

setTimeout(() => (this.activeKey = { code: '' }), 1000)
Enter fullscreen mode Exit fullscreen mode

That means, that after 1000 milliseconds (1 sec), activeState will be cleared.

Result:

Image description

Looking good, the active key automatically disappears after 1 sec.

But there is another problem. When we type fast several keys in 1 sec, only one timer works, that started after pressing the first button. If we type 1, 2, 3, 4, 5 in 900 milliseconds, 5 will disappear after 100 milliseconds, which is incorrect.

Image description

We respect 5 (as any other key) and will give to it the whole 1 second. To do that we need to store a particular timeout when key pressed, and if another key is pressed before timeout ended, we'll clear old timeout and create a new one. That will guarantee 1 sec for any key.

App.js

replace

setTimeout(() => (this.activeKey = { code: '' }), 1000)
Enter fullscreen mode Exit fullscreen mode

with

/* if there was old timeout, we clear it*/
clearTimeout(this.timeout)
/* store a new timeout for the last pressed key */
this.timeout = setTimeout(() => (this.activeKey = { code: '' }), 1000)
Enter fullscreen mode Exit fullscreen mode

Now 5 also has 1 sec to show itself to the world:

Image description

Diffs in code 8.2

Entire code after the chapter

πŸ’– πŸ’ͺ πŸ™… 🚩
apayrus
Rustam Apay

Posted on August 18, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related