具体实现

contenteditable 属性规定元素内容是否可编辑。属性说明

效果:
在这里插入图片描述
先说一下主要思路:
将填空部分设置为可编辑元素,通过监听input事件获取填空部分的innerText。通过text-decoration: underline;设置底部横线。
填空题初始状态在题目后面需要预留一段空白,因为做的是内联填空,所以可编辑元素不能设置为inline-block,否则长度超出一行后,填空内容会独占一行,与题目分成两行,体验极差。那么自然也不能设置最小宽度,所以在可编辑元素后面新增一段占位空格,在进行输入时相对应减少占位空格数量。

以下是填空组件fillblank.vue内容
模板:

<p class="other-input-text">
    <span class="label">{{ label }}</span>
    <span class="value seat" ref="value" contenteditable @blur="blur" @input="input" @focus="focus"></span>
    <span class="seat" @click="clickSeat">{{ seat }}</span>
    <span v-if="comma"></span>
</p>

js代码:

export default {
    props: {
        // 题目label
        label: {
            type: String
        },
        // 填空内容
        value: {
            type: String
        },
        // 末尾是否显示逗号
        comma: {
            type: Boolean
        }
    },
    model: {
        prop: 'value',
        event: 'changeValue'
    },
    data() {
        return {
            inputValue: ''
        };
    },
    computed: {
        // 占位空格内容
        seat() {
            let minLength = 15;
            let detaLength = minLength - this.inputValue.length;

            if (detaLength > 0) {
                return new Array(detaLength).fill(' ').join('');
            }

            return '';
        }
    },
    watch: {
        value(val) {
            this.inputValue = val;
        },
        inputValue(value) {
            this.$emit('changeValue', value);
        }
    },
    mounted() {
        this.inputValue = this.value || '';
        this.$refs.value.innerHTML = this.inputValue;
    },
    methods: {
        // 点击占位空格时聚焦可编辑元素
        clickSeat() {
            this.$refs.value && this.$refs.value.focus();
        },
        // 聚焦时定位光标位置
        focus(e) {
            setTimeout(() => {
                this.setFocus(this.inputValue.length);
            }, 0);
        },
        // 失焦时再赋值一遍
        blur() {
            this.inputValue = this.$refs.value.innerText;
            console.log(this.$refs.value.innerText);
        },
        // 输入时进行赋值
        input() {
            this.inputValue = this.$refs.value.innerText;
        },
        // 聚焦后把光标放到最后
        setFocus(index) {
            let obj = this.$refs.value;

            if (document.createRange) {
                // 获取选定对象
                let selection = getSelection();
                let range = document.createRange();

                range.setStart(obj.firstChild || obj, index);
                range.setEnd(obj.firstChild || obj, index);
                selection.removeAllRanges();
                selection.addRange(range);
            }
        }
    }
};

样式部分的代码就不贴了,因为做的是内联填空,需要注意以下几点:
1、整个组件中元素的样式都应该是内联的;
2、字母单词需要可以换行;
3、填空部分及占位部分需要添加下划线;
4、可编辑对象的边框可使用outline:none隐藏。

其他知识点

在实现过程中,为了优化体验,也对光标的位置手动做了定位,每次可编辑元素进行聚焦的时候,都让光标出现在填空内容的末尾。这涉及到selection对象和range对象,以下是参考的博文:

range对象

Logo

前往低代码交流专区

更多推荐