添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
没有腹肌的甘蔗  ·  Entity Framework 6 ...·  8 月前    · 
逼格高的韭菜  ·  Vue scss ...·  1 年前    · 

双击编辑文字,对于一些简单的需求就很简单,双击之后把div换成input,鼠标失去焦点之后隐藏input换回div就好了。

but 本文要讲的是复杂一点的需求,主要用于一些编辑系统,或者低代码平台的编辑文字组件,属于一种基础组件吧。 类似于这样:

总体思路其实很简单

  • 两个区域,一个展示,一个编辑
  • 双击(dbclik)展示的dom,切换成编辑区域,并选中文字(使用rangeApi)
  • 失去焦点(blur),变回展示区域
  • 花了个图:

    有一些要点需要说明下:

  • contenteditable 该属性让div可以编辑
  • 选中文字使用了浏览器的range api
  • 监听paste事件,添加了对粘贴文字的处理,主要是去除多余的格式
  • css样式也要注意一下,white-space: pre-line;让/n在div中可以换成 word-break: break-all; 让文字长度超出div之后断行
  • 有问题看代码吧:

    <template>
        <section class="element-box" :style="boxStyle">
            <!--  展示部分-->
                class="show-text"
                :style="textStyle"
                v-if="!isEdit"
                @dblclick="editText"
                v-html="text"
            ></div>
            <!--  编辑部分-->
            <div ref="text" class="textbox" v-if="isEdit">
                    class="textbox-container"
                    contenteditable="true"
                    @blur="updateText()"
                    id="textbox"
                    :style="textStyle"
                    v-html="text"
                ></div>
            </div>
        </section>
    </template>
    <script>
    export default {
        name: 'edit-text-test',
        data() {
            return {
                isEdit: false,
                text: '双击编辑文字',
                boxInfo: {
                    width: 200,
                    height: 36
        computed: {
            //文字的样式
            textStyle() {
                return {
                    fontSize: '24px',
                    textAlign: 'center'
            //容器的宽高
            boxStyle() {
                return {
                    width: this.boxInfo.width + 'px',
                    height: this.boxInfo.height + 'px'
        methods: {
            editText() {
                this.isEdit = true
                setTimeout(() => {
                    //选中文字
                    if (window.getSelection) {
                        let selection = window.getSelection()
                        let range = document.createRange()
                        range.selectNodeContents(document.getElementById('textbox'))
                        selection.removeAllRanges()
                        selection.addRange(range)
                    //粘贴时,去除多余格式
                    document
                        .getElementById('textbox')
                        .addEventListener('paste', e => {
                            e.preventDefault()
                            e.stopPropagation()
                            let text
                            let clp = (e.originalEvent || e).clipboardData
                            if (clp === undefined || clp === null) {
                                text = window.clipboardData.getData('text') || ''
                                if (text !== '') {
                                    if (window.getSelection) {
                                        let newNode = document.createElement('span')
                                        newNode.innerHTML = text
                                        window
                                            .getSelection()
                                            .getRangeAt(0)
                                            .insertNode(newNode)
                                    } else {
                                        document.selection
                                            .createRange()
                                            .pasteHTML(text)
                            } else {
                                text = clp.getData('text/plain') || ''
                                if (text !== '') {
                                    document.execCommand('insertText', false, text)
                }, 100)
            updateText() {
                let modifiedText = this.$refs.text.childNodes[0].innerHTML
                    .replace(/<div><br></div>/g, '\n')
                    .replace(/<div.*?>/g, '\n')
                    .replace(/<br.*?>/g, '')
                    .replace(/</div>|&nbsp;|</?span.*?>/g, '')
                this.isEdit = false
                // 文字内容没修改不update
                if (this.text !== modifiedText) {
                    if (modifiedText === '') modifiedText = '双击编辑文字'
                    this.text = modifiedText
                    this.updateBoxSize()
            updateBoxSize() {
                //更新下父容器的高度
                this.boxInfo.height = document.getElementById(
                    'textbox'
                ).offsetHeight
    </script>
    <style lang="less" scoped>
    .element-box {
        position: relative;
    .show-text {
        word-break: break-all;
        white-space: pre-line;
    .textbox {
        position: absolute;
        width: 100%;
        height: 100%;
        top: 0;
        left: 0;
        .textbox-container {
            width: 100%;
            position: absolute;
            border: 1px dashed #000;
            height: auto;
            word-break: break-word;
            outline: none;
            white-space: pre-wrap;
            box-sizing: content-box;
            -webkit-user-select: text !important;
            user-select: text !important;
            -webkit-background-clip: text;
            caret-color: black;
    </style>
    

    双击编辑文字的逻辑大概都是这样的,我是用vue写的,其实可以很方便的改成react或者其他框架的语法。

    希望本文可以解决一些人的需求。

    写文本大约耗费了两局王者荣耀的时间,求一下点赞。

    算了,不求赞了,这篇文章受众太小了,估计都没什么人看、就酱紫,写完收工。

    分类:
    前端
    标签: