前言
在开发微信小程序时,使用 textarea
组件可能会遇到一些棘手的问题。最近我在使用 uniapp 开发微信小程序时,就遇到了两个非常令人头疼的问题:
- 层级穿透:由于
textarea
是原生组件,任何元素都无法遮盖住它。当其他元素与textarea
重叠时,就会出现层级穿透的问题。特别是在弹窗中使用textarea
时,如果弹窗有动画效果,textarea
会在动画执行期间显示在页面右上角,导致视觉效果非常差。如果等到动画结束后再显示textarea
,又会出现突然弹出的效果,影响用户体验。 - 光标位置不正确:在 iOS 设备上,
textarea
的光标默认会出现在文本的最前面。当文本较长时,点击文本中间位置时光标仍然会出现在最前面,弹出键盘后很难重新定位到需要编辑的位置,使用体验非常糟糕。
为了解决这些问题,我尝试了多种方案,但效果都不尽如人意。最终,我发现可以使用 editor
富文本编辑器组件来替代 textarea
,它不仅解决了上述问题,还提供了更多的自定义选项。
封装 Editor 组件
editor
富文本编辑器组件不仅可以实现多行文本输入,还能避免层级穿透和光标位置错误的问题,甚至可以自定义光标的颜色。
官方对 editor
组件的介绍如下:
富文本编辑器,可以对图片、文字格式进行编辑和混排。
既然 editor
是一个富文本编辑器,那么它自然可以处理带有格式的文本。我们可以通过降级使用,将其作为一个普通的文本输入框,输入和输出的内容都是纯文本。
我的实现代码如下:
<template>
<view class="relative">
<!-- 编辑器区域 -->
<editor
:id="editorId"
:model-value="htmlContent"
class="w-full bg-gray-50/50 rounded-xl p-3 text-sm min-h-[120px] text-base"
:placeholder="placeholder || '请输入内容...'"
@ready="onEditorReady"
@input="handleInput"
:read-only="isReadOnly"
/>
</view>
</template>
<script setup lang="ts">
import { EditorContext } from '@/types/global'
import nanoid from '@/utils/nanoid'
const props = defineProps<{
modelValue: string
placeholder?: string
id?: string
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: string): void
(e: 'blur'): void
}>()
// 编辑器内部 HTML 内容
const htmlContent = ref(props.modelValue)
// 编辑器上下文
const editorCtx = ref<EditorContext | null>(null)
const isReadOnly = ref(true)
// 生成唯一 id
const editorId = computed(() => props.id || `editor-${nanoid()}`)
const instance = getCurrentInstance()
// 初始化编辑器
const onEditorReady = () => {
uni
.createSelectorQuery()
.in(instance)
.select(`#${editorId.value}`)
.context((res: any) => {
editorCtx.value = res.context
// 只设置初始内容,不自动聚焦
if (editorCtx.value && props.modelValue) {
editorCtx.value.setContents({ html: props.modelValue })
}
isReadOnly.value = false
})
.exec()
}
// 处理输入
const handleInput = (e: any) => {
// 保存 HTML 内容
htmlContent.value = e.detail.html || ''
// 向外发送纯文本
emit('update:modelValue', e.detail.text || '')
}
defineExpose({
editorCtx,
})
</script>
<style scoped>
:deep([id^='editor']) {
caret-color: rgb(99, 102, 241);
}
</style>
如果你有需要可以直接复制使用,作为组件引入。这里面有几个注意点我介绍一下:
- 默认的
readonly
是true
,在编辑器准备完成后修改为true
,这个逻辑是因为setContent
设置内容时会自动聚焦,设置为readonly: false
就不会触发自动聚焦了,如果你需要自动聚焦可以将这段逻辑移除掉。 - 默认暴露了
editorCtx
如果你想主动修改编辑器内容可以触发editorCtx.setContent
去修改,而不是直接修改modelValue
,因为内部并没有监听modelValue
的实时变化去修改编辑器,避免多处地方同时修改编辑器内容。 :deep([id^='editor']) { caret-color: rgb(99, 102, 241); }
:用于修改编辑器中光标颜色,因为我这里支持自定义id
,所以匹配了所有editor
前缀。
通过将 textarea
替换为 editor
后,你会发现效果都是一样的,而且在 ios
端的使用体验会好很多,手指点击哪里,光标的默认位置就在哪里,在效果如下图所示。
💡注意点:
editor
组件在focus
时在 ios 设备上是不会获取到键盘高度的,因此如果你有工具栏放在键盘上的需求,在替换组件时需要慎重考虑。
总结
如果你想查看 editor
组件的实际效果,可以搜索学习卡盒小程序,在卡片页面长按编辑尝试一下,看看效果是否符合你的需求。希望这篇文章能帮助你解决遇到的问题。如果对你有帮助,欢迎点赞支持!