howenhuo

17 天前

小程序textarea与弹窗

本文作者:IMWeb howenhuo 原文出处:IMWeb社区 未经同意,禁止转载

前言

在小程序 textarea 组件上展示一个模态弹窗组件,会发现 textarea 中输入的文字内容,会直接穿透模态弹窗显示在最上面,而且还能点击输入文字。

限制

小程序 textarea 是由客户端创建的原生组件,由于原生组件脱离在 WebView 渲染流程外,因此在使用时有以下限制:

  • 原生组件的层级是最高的,所以页面中的其他组件无论设置 z-index 为多少,都无法盖在原生组件上。
    • 后插入的原生组件可以覆盖之前的原生组件。
  • 原生组件还无法在 picker-view 中使用。
  • 部分CSS样式无法应用于原生组件,例如:
    • 无法对原生组件设置 CSS 动画
    • 无法定义原生组件为 position: fixed
    • 不能在父级节点使用 overflow: hidden 来裁剪原生组件的显示区域
  • 原生组件的事件监听不能使用 bind:eventname 的写法,只支持 bindeventname。原生组件也不支持 catch 和 capture 的事件绑定方式。
  • 原生组件会遮挡 vConsole 弹出的调试面板。 在工具上,原生组件是用web组件模拟的,因此很多情况并不能很好的还原真机的表现,建议开发者在使用到原生组件时尽量在真机上进行调试。

那么要在 textarea 上正常的覆盖一个弹窗,该如何做呢?

解决方案

方法一:cover-view 和 cover-image

为了解决原生组件层级最高的限制。小程序专门提供了 cover-viewcover-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 被隐藏了。

<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>
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),但切换时由于会拉起软键盘,几乎忽略了闪动的视觉影响。可是,该方案有个比较难解决的问题就是,进入编辑状态时光标无法定位到用户点击的位置(一般自动聚焦到末尾),需要用户二次点击定位。

<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 展示在顶部,弹窗展示在底部,或者改为侧边栏呼出弹出层等等替换的交互设计。

0条评论

    您需要 注册 一个IMWeb账号或者 才能进行评论。