preface

Due to the need of work, I have been working on the development of TV set-top box APP recently. So the focus control is componentized.

In order to improve the development speed, we use app shell, which internally refers to the development mode of H5 page.

Abstract

Development environment: Set-top box, Android system

Front-end framework: VUE-CLI3, VUe2, VUex, vuE-Router

The body of the

A, environmental

1. Focus acquisition in set-top box environment:

The remote control can automatically retrieve focused DOM elements by moving them up, down, left, and right. Dom elements that can get focus by default can be selected by the remote control, and elements that do not have focus can be obtained by setting the tabIndex property.

2. App and Android environment introduction

For security reasons, the Android version of the set-top box will not be mentioned here, except that it is a lower version. We can support some ES6 syntax after using the app shell. Here again mention the difference between the browser, and the STYLE of chrome browser on the PC is different, after the completion of development, be sure to run on the set-top box to see the effect.

3. Why focus control

There is no focus control problem with routing pages, because only the focus of non-hidden elements (display: None and its children do not work) can be selected by the remote control. However, our project requires a popover function, and in this case, we need to control the focus of the parent page, because the mask does not prevent the focus from being obtained.

Second, the implementation

My design idea is to achieve global focus control through vuEX’s responsive programming function. This is achieved by encapsulating all the elements that need focus as components, and managing focus through computed attributes in the component to listen for changes in global focus in VUEX.

1. Implementation in VUEX

Create a tabindex.js file that declares the focus control-related attributes: enable, enablePage, and markEnable

const tabindex = {
    namespaced: true.state: {
        enable: true.// Global focus switch
        enablePage: [].// Get the pageName of the focus page
        markEnable: true // Mask: Can't get focus when the mask is displayed.
    },
    mutations: {
        SET_ENABLE: (state, enable) = > {
            state.enable = enable;
            console.log("SET_ENABLE", state.enable);
        },
        SET_MARK_ENABLE: (state, enable) = > {
            state.markEnable = enable;
            console.log("SET_MARK_ENABLE", state.markEnable);
        },
        PUSH_TABINDEX: (state, enablePage) = > {
            if(! state.enablePage.includes(enablePage)) { state.enablePage.push(enablePage); }console.log("PUSH_TABINDEX", state.enablePage);
        },
        REMOVE_TABINDEX: (state, enablePage) = > {
            let index = state.enablePage.indexOf(enablePage); 
            if (index > -1) { 
                state.enablePage.splice(index, 1);
            }
            console.log("REMOVE_TABINDEX", state.enablePage); }},actions: {
        disabled({commit}, enablePage){
            return new Promise(resolve= > {
                commit('SET_ENABLE'.false);
                commit('PUSH_TABINDEX', enablePage);
                resolve();
            });
        },
        enable({commit}, enablePage){
            return new Promise(resolve= > {
                commit('REMOVE_TABINDEX', enablePage);
                if (state.enablePage.length <= 0) {
                    commit('SET_ENABLE'.true);
                }
                resolve();
            });
        },
        markDisabled({commit}){
            return new Promise(resolve= > {
                commit('SET_MARK_ENABLE'.false);
                resolve();
            });
        },
        markEnable({commit}){
            return new Promise(resolve= > {
                commit('SET_MARK_ENABLE'.true); resolve(); }); }}};export default tabindex;
Copy the code
  1. Enable: Controls whether the global focus is available
  2. EnablePage: Used to record the name of the pageName property of the page that can be excepted if global focus is not available
  3. MarkEnable: All focal points are unavailable, such as during loading

Note: The pageName property is a custom props property, which can be assigned in the popover component or generated randomly by the math.random () method and will be mentioned in the popover build.

2. Implementation in components

You can control the tabIndex property of a component by listening for changes in vuEX properties in the component

<template>
    <div :class="getClass()" :tabindex="computedTabindex" ref="main" v-on="$listeners" >
        <slot></slot>
    </div>
</template>
<script>
export default {
    name: "box-tabindex-box".props: {
        tabindex: {
            type: Number.default: 1
        },
        focusFlag: {
            type: Boolean.default: false
        },
        useFocusClass: {
            type: Boolean.default: true}},inject: {
        pageName: {
            default: ""}},computed: {
        computedTabindex: function(){
            let tabindex = this.$store.state.tabindex;
            if (tabindex.markEnable === false) {
                return -1;
            }
            if (tabindex.enable === false) {
                if (tabindex.enablePage[tabindex.enablePage.length - 1] = = (this.pageName)) {
                    return this.tabindex;
                }
                return -1;
            }
            return this.tabindex; }},mounted(){
        let that = this;
        this.$nextTick(function(){
            if (this.focusFlag) { that.$refs.main.focus(); }}); },methods: {
        getClass(){
            if (this.useFocusClass) {

                return "u-tab-box";
            } else {

                return ""; }}}}</script>
<style lang="scss" >
    .u-tab-box{}.u-tab-box:focus{
        background-color: rgba(0.0.0.0.4);
        box-sizing: border-box;
    }
</style>
Copy the code

There may be other methods to monitor vuEX changes through computed attributes in VUE.

The input box can also be used for focus control in the same way as above, so I won’t go into details here.

3. Popover components

The popover component needs to add a pageName property to identify the popover page. At the same time, provide and inject of the component are matched to send parameters so that the component knows which page it is on.

<template>
    <div class="g-mark" v-show="params.openFlag === true" >
        <div class="g-win menu-background bg-image" :class="winClass" >
            <slot></slot>
        </div>
    </div>
</template>
<script>

export default {
    name: "open-win".props: {
        pageName: {
            type: String.default: "win: " + Math.random()
        },
        winClass: {
            type: String.default: ""}},data(){
        return {
            params: {
                openFlag: false}}},provide() {
        return {
            pageParams: this.params,
            pageName: this.pageName
        };
    },
    watch: {
        "params.openFlag"(newVal){
            if (newVal === true) {
                console.log("tabindex/disabled".this.pageName);
                this.$store.dispatch("tabindex/disabled".this.pageName);

            } else {
                console.log("tabindex/enable".this.pageName);
                this.$store.dispatch("tabindex/enable".this.pageName); }}},methods: {
        open(){
            this.params.openFlag = true;
        },
        close(){
            this.params.openFlag = false;
            this.$emit("winClose"); }}}</script>
<style scoped>
.g-mark {
    position: absolute;
    top: 0;
    left: 0;
    background-color: rgba(0.0.0.0.2);
    display: flex;
    align-items:center;
    justify-content:center;
    width: 100%;
    height: 100%;
    z-index: 99999;
}
.g-win{
    display: flex;
    min-width: 800px;
    min-height: 600px;
}
</style>
Copy the code

4. Page implementation

Page by introducing the box-open-win component and setting the pageName property, of course you can not set, the component will automatically generate a random name

      <box-open-win ref="selectUser" pageName="SelectUser" winClass="user-win" >
          <select-user />
      </box-open-win>
Copy the code

Remark:

1. If you cannot select other elements after the focus is obtained, it may be due to overflow: Scroll style of the parent element, because the scrolling effect takes precedence over the focus movement when you press the arrow key.