四川省建设厅招标网站,网页设计与制作怎么做清平调代码,唐山网站建设赫鸣科技,鲜花网站建设的主要工作流程简介
本文是基于 VUE3TS 的代码说明。
记录自己遇到的 div 与 textarea 输入框交互的聚焦、失去焦点、键盘收起、表情插入不失去焦点的需求实现。
需求分析 1.固定在页面底部#xff1b; 2.默认显示纯文字与发送图标按钮#xff0c;文字超出的省略显示#xff1b; 3.点击…简介
本文是基于 VUE3TS 的代码说明。
记录自己遇到的 div 与 textarea 输入框交互的聚焦、失去焦点、键盘收起、表情插入不失去焦点的需求实现。
需求分析 1.固定在页面底部 2.默认显示纯文字与发送图标按钮文字超出的省略显示 3.点击文字后显示文本输入框、表情、半透明遮罩层自动聚焦 4.有输入内容时文本输入框右侧显示发送按钮 5.点击表情将表情加入到输入框最后并且输入法键盘不收起 6.输入框失去焦点、点击键盘上的收起或完成时隐藏文本输入框和表情显示默认的纯文字样式。
注意
以下代码是伪代码
1.输入框聚焦后可能存在输入框位置不正确的问题
如输入框被遮挡、输入框没有挨着键盘等类似的问题。 这些问题在网上的解决方案较多可自行查阅。
我的处理思路如下 1在 app.vue 中建立一个传送目标
templaterouter-view /!-- 这里是输入框显示时传送的目标DOM --div idinputPosition /
/template2用 Teleport 标签包裹输入框的html代码文本框是否需要显示用变量控制 当然传输的内容最大层需要定位到底部注意样式层级 这样之后输入框的输入法键盘弹起不会对列表产生影响不会出现兼容性问题。
// html
Teleport to#inputPositiondiv v-showisTextareaFocus classtextarea-box!-- 输入框与发送按钮 --divtextarea reftextareaRef /button发送/button/div!-- 表情 --divdiv v-for(emoji, index) in emojiList :keyindex{{ emoji }}/div/div/div
/Teleport3点击文本div时显示文本输入框并且自动聚焦
script setup langtsimport { ref, nextTick } from vueconst isTextareaFocus ref(false) // 文本输入框是否聚焦即显示const textareaRef ref() // 输入框对应的DOMconst emojiList [, , , , , , , ] // , /** 方法输入框文本-是否聚焦、显示 */const displayTextarea (display false) {isTextareaFocus.value display}/** 操作点击文本div */const handleToFocus () {displayTextarea(true)nextTick(() {textareaRef.value?.focus() // 聚焦})}
/script2.键盘按钮的收起判断输入框是否失去焦点
1Android上键盘按钮的收起大部分不会触发输入框的blur事件会触发webview的改变 2IOS上键盘按钮的收起会触发输入框的blur事件大部分不会触发webview的改变 3点击表情时也会导致输入框失去焦点。
我的处理思路如下 1判断到是Android时添加全局监听根据webview的变化比对判断是键盘弹出还是收起收起时隐藏文本输入框及表情 2由于touchStart会先于click事件而click事件会触发输入框的聚焦与失去焦点的事件所以对dom添加touchStart事件若是表情或发送按钮触发的则标记不清除输入框的聚焦否则标记需要清除然后在触发输入框的blur事件中判断该标记状态是否执行隐藏的逻辑这是用于IOS
textarea reftextareaRef blurtextareaBlur /import { ref, onMounted, onBeforeUnmount }const isNeedFocus ref(true) // 是否需要焦点const textareaBlur () {if (isNeedFocus.value) {isNeedFocus.value falsereturn}displayTextarea(false)}/** 操作键盘弹出时点击蒙层关闭输入 */const clickBlur () {if (textareaRef.value) {textareaRef.value.blur()}displayTextarea(false)}/** 获取可视高度 */const getClientHeight () {return document.documentElement.clientHeight || document.body.clientHeight}let origin getClientHeight()const compatibilityFn () {const resize getClientHeight()if (origin resize) {const focusEl textareaRef.valueif (focusEl) {focusEl.scrollIntoView({ behavior: smooth, block: center })}} else {// 和ios保持一致: 键盘收起之后去掉聚焦clickBlur()}origin resize}const getUserTerminalType (): UserTerminalEnum {const u navigator.userAgentconst isAndroid u.indexOf(Android) -1 || u.indexOf(Adr) -1 // 判断是否是 android终端const isIOS !!u.match(/\(i[^;];( U;)? CPU.Mac OS X/) // 判断是否是 iOS终端if (isAndroid) {return UserTerminalEnum.ANDROID}if (isIOS) {return UserTerminalEnum.IOS}return UserTerminalEnum.WEB}const isNotIOS getUserTerminalType() ! UserTerminalEnum.IOSconst compatibilityOfTextarea () {if (!isNotIOS) {return}// 挂载事件到全局window.addEventListener(resize, compatibilityFn)}onMounted(() {compatibilityOfTextarea()})onBeforeUnmount(() {if (isNotIOS) {// 移除挂载到全局的事件window.removeEventListener(resize, compatibilityFn)}})3.表情的插入
给整个列表、文本输入框盒子添加touchstart事件最先执行的是touchstart根据当前touch事件的触发dom的id判断是否需要保留文本输入框的聚焦然后执行的表情的点击事件以及文本输入框的失去焦点事件其中 1touchStartEvent 判断触发的dom的id是否是需要保留聚焦的dom做一个标记 2handleInsertEmoji 做表情的插入以及对文本输入框的聚焦 3handleToBlur 做输入框失去焦点的逻辑处理根据1中的标记进行逻辑处理之所以要重置标记是为了下次输入框能正常失去焦点。
// html
div classpage touchstarttouchStartEvent...!-- 文本输入框、表情栏 --Teleport to#inputPositiondiv v-showisTextareaFocus classtextarea-box touchstarttouchStartEvent...textarea blurhandleToBlur /...!-- 表情 --div classemoji-listdividemojiv-for(emoji, index) in emojiList:keyindexclick.stophandleInsertEmoji(emoji){{ emoji }}/div/div/div/Teleport
/div// ts/** 进行手势操作时的过滤处理如点击、滑动等 */const touchStartEvent (e: any) {// 这里包含textareaBtn是为了发送按钮的点击事件能正常触发if (e.target.id emoji || e.target.id textareaBtn) {isNeedFocus.value true} else {isNeedFocus.value false}}/** 操作表情 */const handleInsertEmoji (emoji: string) {if (message.value.length messageLength) {return}message.value emojinextTick(() {handleToFocus()})}/** 文本输入框失去焦点时的逻辑处理 */const handleToBlur () {if (isNeedFocus.value) {isNeedFocus.value falsereturn}displayTextarea(false)}具体实现
目录结构 /test /test/utils.ts /test/index.vue
1.app.vue
templaterouter-view /!-- 这里是输入框聚焦显示时传送的目标DOM --div idinputPosition /
/templatestyle langless* {margin: 0;padding: 0;}
/style2.utils.ts
enum UserTerminalEnum {ANDROID,IOS,WEB
}/** 获取当前所在客户端的类型 */
const getUserTerminalType (): UserTerminalEnum {const u navigator.userAgentconst isAndroid u.indexOf(Android) -1 || u.indexOf(Adr) -1 // 判断是否是 android终端const isIOS !!u.match(/\(i[^;];( U;)? CPU.Mac OS X/) // 判断是否是 iOS终端if (isAndroid) {return UserTerminalEnum.ANDROID}if (isIOS) {return UserTerminalEnum.IOS}return UserTerminalEnum.WEB
}const isNotIOS getUserTerminalType() ! UserTerminalEnum.IOSexport { UserTerminalEnum, isNotIOS }
3.index.vue
templatediv classpage touchstarttouchStartEvent!-- 遮罩 --div v-ifisTextareaFocus classmask-box touchstartclickBlur /!-- 文字展示栏 --div classinput-areadiv classinput-text-box!-- 文字展示 --div classinput-text clickhandleToFocus{{ message || placeholderText }}/div!-- 发送图标按钮 --divclassbtn-input:class{ btn-input-active: message?.length }clickhandleSend//div/div!-- 文本输入框、表情栏 --Teleport to#inputPositiondiv v-showisTextareaFocus classtextarea-box touchstarttouchStartEvent!-- 输入框与发送按钮 --div classtextarea-rowtextareareftextareaRefv-modelmessage:classmessage.length ? textarea-none : textarea-activeclasstextarea-normal:placeholderplaceholderTextstyletransition-duration: 0.2s;transition-timing-function: ease;-webkit-user-select: text !important;:contenteditabletruenametextarearows5cols50:maxlengthmessageLengthblurhandleToBlur/buttonidtextareaBtn:style{opacity: message.length ? 1 : 0,transition-delay: message.length ? 200ms : 0ms}click.stophandleSend发送/button/div!-- 表情 --div classemoji-listdividemojiv-for(emoji, index) in emojiList:keyindexclick.stophandleInsertEmoji(emoji){{ emoji }}/div/div/div/Teleport/div
/templatescript setup langtsimport { ref, nextTick, onMounted, onBeforeUnmount } from vueimport { isNotIOS } from ./utilsconst placeholderText ref(尽情反馈您的建议哦~)const message ref() // 输入框内容const isTextareaFocus ref(false) // 文本输入框是否聚焦即显示const textareaRef ref() // 输入框对应的DOMconst messageLength 200const emojiList [, , , , , , , ] // , const isNeedFocus ref(true) // 是否需要焦点/** 方法输入框文本-是否聚焦、显示 */const displayTextarea (display false) {isTextareaFocus.value display}/** 操作点击文本div */const handleToFocus () {displayTextarea(true)nextTick(() {textareaRef.value?.focus() // 聚焦})}/** 文本输入框失去焦点时的逻辑处理 */const handleToBlur () {if (isNeedFocus.value) {isNeedFocus.value falsereturn}displayTextarea(false)}/** 进行手势操作时的过滤处理如点击、滑动等 */const touchStartEvent (e: Event) {const target e.target as HTMLElement// 这里包含textareaBtn是为了发送按钮的点击事件能正常触发if (target.id emoji || target.id textareaBtn) {isNeedFocus.value true} else {isNeedFocus.value false}}/** 操作键盘弹出时点击蒙层关闭输入 */const clickBlur () {if (textareaRef.value) {textareaRef.value.blur()}displayTextarea(false)}/** 操作表情 */const handleInsertEmoji (emoji: string) {if (message.value.length messageLength) {return}message.value emojinextTick(() {handleToFocus()})}/** 操作点击发送 */const handleSend () {console.log(发送消息)message.value }/** 获取可视高度 */const getClientHeight () {return document.documentElement.clientHeight || document.body.clientHeight}let origin getClientHeight()const compatibilityFn () {const resize getClientHeight()if (origin resize) {const focusEl textareaRef.valueif (focusEl) {focusEl.scrollIntoView({ behavior: smooth, block: center })}} else {// 和ios保持一致: 键盘收起之后去掉聚焦clickBlur()}origin resize}const compatibilityOfTextarea () {if (!isNotIOS) {return}// 挂载事件到全局window.addEventListener(resize, compatibilityFn)}onMounted(() {compatibilityOfTextarea()})onBeforeUnmount(() {if (isNotIOS) {// 移除挂载到全局的事件window.removeEventListener(resize, compatibilityFn)}})
/scriptstyle scoped langless.page {width: 100vw;height: 100vh;position: relative;background-color: #141624;.mask-box {position: absolute;top: 0;right: 0;bottom: 0;left: 0;opacity: 0.5;background-color: #000;}.input-area {height: 82px;padding: 10px 12px 0px;position: absolute;right: 0;bottom: 0;left: 0;border-top: 1px solid #272937;background-color: #141624;.input-text-box {height: 40px;padding: 0 15px;border-radius: 20px;background-color: #272937;display: flex;align-items: center;.input-text {flex: 1;line-height: 40px;font-size: 16px;color: #939191;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;}.btn-input {margin-left: 10px;width: 22px;height: 22px;border-radius: 5px;background-color: #939191;}.btn-input-active {background-color: #3994f9;}}}}.textarea-box {position: absolute;right: 0;bottom: 0;left: 0;z-index: 9999;border-top: 1px solid #272937;background-color: #141624;.textarea-row {display: flex;align-items: flex-end;position: relative;padding: 10px;.textarea-normal {padding: 10px;height: 90px;background-color: #272937;color: #fff;border: none;outline: none;inline-size: none;resize: none;border-radius: 8px;font-size: 15px;}.textarea-none {width: calc(100% - 92px);transition-delay: 0ms;}.textarea-active {width: calc(100% - 20px);transition-delay: 200ms;}#textareaBtn {width: 62px;height: 31px;line-height: 31px;text-align: center;position: absolute;right: 10px;bottom: 10px;border-radius: 15px;border: none;background-color: #3994f9;overflow: hidden;white-space: nowrap;color: #fff;font-size: 15px;transition-duration: 0.2s;transition-timing-function: ease;}}.emoji-list {height: 50px;display: flex;align-items: center;#emoji {width: calc(100% / 8);height: 100%;text-align: center;font-size: 30px;}}}
/style
最后
觉得有用的朋友请用你的金手指点一下赞或者评论留言一起探讨技术