本文作者:IMWeb howenhuo 原文出处:IMWeb社区 未经同意,禁止转载
前言
在小程序 textarea 组件上展示一个模态弹窗组件,会发现 textarea 中输入的文字内容,会直接穿透模态弹窗显示在最上面,而且还能点击输入文字。
限制
小程序 textarea
是由客户端创建的原生组件,由于原生组件脱离在 WebView 渲染流程外,因此在使用时有以下限制:
- 原生组件的层级是最高的,所以页面中的其他组件无论设置
z-index
为多少,都无法盖在原生组件上。- 后插入的原生组件可以覆盖之前的原生组件。
- 原生组件还无法在 picker-view 中使用。
- 基础库 2.4.4 以下版本,原生组件不支持在 scroll-view、swiper、movable-view 中使用。
- 部分CSS样式无法应用于原生组件,例如:
- 无法对原生组件设置 CSS 动画
- 无法定义原生组件为
position: fixed
- 不能在父级节点使用
overflow: hidden
来裁剪原生组件的显示区域
- 原生组件的事件监听不能使用
bind:eventname
的写法,只支持bindeventname
。原生组件也不支持 catch 和 capture 的事件绑定方式。 - 原生组件会遮挡 vConsole 弹出的调试面板。 在工具上,原生组件是用web组件模拟的,因此很多情况并不能很好的还原真机的表现,建议开发者在使用到原生组件时尽量在真机上进行调试。
那么要在 textarea 上正常的覆盖一个弹窗,该如何做呢?
解决方案
方法一:cover-view 和 cover-image
为了解决原生组件层级最高的限制。小程序专门提供了 cover-view 和 cover-image 组件,可以覆盖在部分
原生组件上面。这两个组件也是原生组件,但是使用限制与其他原生组件有所不同。cover-view 只支持嵌套 cover-view、cover-image、button、navigator、ad 等组件,其余组件在真机上都会被忽略。所以如果弹窗中要显示 input、radio、checkbox 等组件的话,该方案无法实现,但对于只是展示文本、按钮和图片的话,还是可以满足的。
<textarea value="{{text}}"></textarea>
<cover-view class="modal modal-fixed">
<cover-view class="dialog">
弹窗弹窗弹窗弹窗
<button>按钮</button>
</cover-view>
</cover-view>
方案二:弹窗出现时隐藏 textarea
该方案适合弹窗尺寸能够覆盖整个 textarea 或者弹窗的模态层是不透明的,这样即便 textarea 隐藏了,对用户来说无感知,当弹窗消失以后再把 textarea 显示出来。需要注意的是对于带半透明模态层的弹窗,其所在的页面内容最好不要超过屏幕高度,否则页面滚动会让用户知道 textarea 被隐藏了。
代码语言:javascript复制<textarea wx:if="{{ !showDialog }}"></textarea>
<view class="modal modal-fixed" wx:if="{{showDialog }}">
<view class="dialog">
弹窗弹窗弹窗弹窗
<button>按钮</button>
</view>
</view>
方案三:弹窗出现时使用 view 替换 textarea
该方案是方案二的优化,能够保证 textarea 的内容仍然展示,但由于原生组件和 WebView 的差异,无法保证 textarea 和 view 组件展示的一致性,尤其是 字体
,某些 Android 机型 textarea 和 view 的字体展示不一样,即使设置了 Android 的系统字体 font-family: -apple-system-font, "Helvetica Neue", sans-serif;
,所以在弹窗出现时,textarea 和 view 切换会有较为明显的闪动,适当调低模态层的透明度可以缓解闪动的视觉影响。
<textarea wx:if="{{!showDialog}}" bindinput="changeText"></textarea>
<view wx:else class="shadow-textarea" style="{{shadowTextStyle}}">
<rich-text space="nbsp" nodes="{{shadowText}}"></rich-text>
</view>
<view class="modal modal-fixed" wx:if="{{showDialog}}">
<view class="dialog">
弹窗弹窗弹窗弹窗
<button>按钮</button>
</view>
</view>
代码语言:javascript复制getShadowText(text, placeholder) {
const placeholderClass = text === '' ? 'placeholder' : '';
// 将换行符转换为wxml可识别的换行元素 <br/>
let shadowText = text === '' ? placeholder : text.replace(/n/g, '<br/>');
shadowText = `<div class="rich-text ${placeholderClass}">${shadowText}</div>`;
return shadowText;
},
changeText(event) {
const text = event.detail.value;
this.setData({
text,
shadowText: this.getShadowText(text, this.properties.placeholder),
});
},
方案四:使用 view 模拟 textarea 常态展示
该方案主要在非编辑状态下使用 view 展示,当点击 view 时,生成 textarea 并进入编辑状态。虽然view 和 textarea 切换后仍然是不同字体(Android),但切换时由于会拉起软键盘,几乎忽略了闪动的视觉影响。可是,该方案有个比较难解决的问题就是,进入编辑状态时光标无法定位到用户点击的位置(一般自动聚焦到末尾),需要用户二次点击定位。
代码语言:javascript复制<textarea wx:if="{{isEdit}}" bindinput="changeText"></textarea>
<view wx:else class="shadow-textarea" style="{{shadowTextStyle}}">
<rich-text space="nbsp" nodes="{{shadowText}}"></rich-text>
</view>
<view class="modal modal-fixed" wx:if="{{showDialog}}">
<view class="dialog">
弹窗弹窗弹窗弹窗
<button>按钮</button>
</view>
</view>
方案五:交互设计避免模态弹窗覆盖 textarea
例如固定高度的 textarea 展示在顶部,弹窗展示在底部,或者改为侧边栏呼出弹出层等等替换的交互设计。