背景:在平板浏览器里,长按选中状态后,出现复制搜索菜单,是浏览器的默认行为
现在的需求是,用户长按文字,选中文字,可划选改变选中,出现搜索按钮,点击搜索按钮,页面进入搜索
本来想使用这个禁用,经测试,无效
阻止浏览器的默认行为,只能通过监听touchstart事件,在事件中 e.preventDefault();这样,那个弹窗就不出来了,但是选中状态也没有了,只能重写, 那只能进行重写文字复制功能,监听触摸事件,模拟长按
<template>
<div class="text_area">
<div class="text-container" @touchstart="mousedownHandler" @touchmove="mouseMoveHandler" @touchend="mouseupHandler" ref="reference">
<span class="text_value">{{value}}</span>
</div>
</div>
</template>
利用 document["caretRangeFromPoint"](x, y) 根据触摸的点位置,设置选中range,设置手柄的位置,监听手柄的移动事件,重新设置选中,根据选区,微淘手柄位置,使手柄不能放在文字上
export default {
props: {
value: {
type: String,
},
},
data() {
return {
mouseStatus: "none",
showMenu: false,
menuPosition: {},
selectText: "",
toucheX: "",
toucheY: "",
Loop: 0,
visible: false,
range: "",
textNode: "",
};
},
mounted() {
const This = this;
document.addEventListener("touchstart", This.destroyDiv);
},
destroyed() {
const This = this;
document.removeEventListener("touchstart", This.destroyDiv);
This.destroyDiv();
},
methods: {
initData() {},
mousedownHandler(e) {
console.log(e);
e.preventDefault();
const This = this;
this.toucheX = e.targetTouches[0].clientX;
this.toucheY = e.targetTouches[0].clientY;
clearTimeout(this.Loop); //再次清空定时器,防止重复注册定时器
this.Loop = setTimeout(
function () {
This.selectCursorWord(e.targetTouches[0]);
}.bind(this),
500
);
console.log("touchstart事件触发了222");
// 清除全局下的回复弹框
},
mouseMoveHandler(e) {
console.log("mousemove");
this.mouseStatus = "mousemove";
const moveX = e.targetTouches[0].clientX;
const moveY = e.targetTouches[0].clientY;
// 解决vivo机型,手指没有move,touchmove事件仍然会调用而导致setTimeout被clear
if (this.toucheX !== moveX || this.toucheY !== moveY) {
// 手指滑动,清除定时器,中断长按逻辑
this.Loop && clearTimeout(this.Loop);
}
},
mouseupHandler(e) {
console.log("mouseupHandler");
clearTimeout(this.Loop); //清空定时器,防止重复注册定时器
},
destroyDiv() {
const This = this;
let achor = document.querySelector(".achor.before");
let achor2 = document.querySelector(".achor.after");
// 搜索
const search = document.querySelector(".text_search_wrap");
achor && document.body.removeChild(achor);
achor2 && document.body.removeChild(achor2);
search && document.body.removeChild(search);
// 清除选中
const sel = window.getSelection();
sel.removeAllRanges();
},
selectCursorWord(e) {
const x = e.clientX;
const y = e.clientY;
let offsetNode;
let offset;
const sel = window.getSelection();
sel.removeAllRanges();
if (document["caretPositionFromPoint"]) {
const pos = document["caretPositionFromPoint"](x, y);
if (!pos) {
return;
}
offsetNode = pos.offsetNode;
offset = pos.offset;
} else if (document["caretRangeFromPoint"]) {
const pos = document["caretRangeFromPoint"](x, y);
console.log(pos, x, y);
if (!pos) {
return;
}
offsetNode = pos.startContainer;
offset = pos.startOffset;
console.log(pos, offset, offsetNode);
} else {
return;
}
if (offsetNode.nodeType === Node.TEXT_NODE) {
const textNode = offsetNode;
this.textNode = textNode;
const content = textNode.data;
const head = (content.slice(0, offset).match(/[-_a-z0-9]+$/i) || [
"",
])[0];
const tail = (content
.slice(offset)
.match(/^([-_a-z0-9]+|[\u4e00-\u9fa5])/i) || [""])[0];
if (head.length <= 0 && tail.length <= 0) {
return;
}
const range = document.createRange();
this.range = range;
range.setStart(textNode, offset - head.length);
range.setEnd(textNode, offset + tail.length);
const rangeRect = range.getBoundingClientRect();
//以这个点为中心点,长20 宽20,看这个选中在不在这个范围里,增加灵敏度
if (
rangeRect.left <= x + 10 &&
rangeRect.right >= x - 10 &&
rangeRect.top <= y + 10 &&
rangeRect.bottom >= y - 10
) {
sel.addRange(range);
this.showCursorAndPop();
}
range.detach();
}
},
showCursorAndPop(rangeRect) {
const rectList = this.range.getClientRects();
console.log(rectList);
// 选中多行时的处理
let before = rectList[0],
after = rectList[rectList.length - 1];
let achor = document.querySelector(".achor.before");
let achor2 = document.querySelector(".achor.after");
const { top, left } = before;
const { bottom, right, height } = after;
console.log(before, after);
if (top == bottom - height && left == right) {
// 重合移除
this.destroyDiv();
return;
}
if (achor) {
achor.style.cssText = `top:${top}px;left:${left}px;`;
} else {
achor = document.createElement("div");
achor.className = "achor before";
achor.style.cssText = `top:${top}px;left:${left}px;`;
document.body.appendChild(achor);
this.addCursorEvent(achor, "before");
}
if (achor2) {
achor2.style.cssText = `top:${bottom - height}px;left:${right}px;`;
} else {
achor2 = document.createElement("div");
achor2.className = "achor after";
achor2.style.cssText = `top:${bottom - height}px;left:${right}px;`;
document.body.appendChild(achor2);
this.addCursorEvent(achor2, "after");
}
// 搜索
let search = document.querySelector(".text_search_wrap");
if (search) {
search.style.cssText = `top:${top - 18}px;left:${
(left + right) / 2
}px;`;
} else {
search = document.createElement("div");
search.innerHTML = "搜索";
search.addEventListener("touchstart", this.remarkClick);
search.className = "text_search_wrap";
search.style.cssText = `top:${top - 18}px;left:${
(left + right) / 2
}px;`;
document.body.appendChild(search);
}
},
addCursorEvent(element, type) {
let move = false;
let currentX, currentY;
const textRect = this.$el
.querySelector(".text_value")
.getBoundingClientRect();
console.log(textRect);
element.addEventListener("touchstart", (e) => {
e.preventDefault();
e.stopPropagation();
console.log(e.targetTouches[0]);
move = true;
const { top, left } = element.style;
currentX = e.targetTouches[0].clientX - parseInt(left);
currentY = e.targetTouches[0].clientY - parseInt(top);
console.log(currentX, currentY);
});
element.addEventListener("touchmove", (e) => {
if (move) {
//
const top = e.targetTouches[0].clientY - currentY;
const left = e.targetTouches[0].clientX - currentX;
// 判断移动是否移出了text_value
if (top >= textRect.top && left >= textRect.left) {
// 判断before 不能超过after after 不能超过before
this.setCusorPosition(
element,
e.targetTouches[0].clientX,
e.targetTouches[0].clientY,
type
);
}
//element.style.cssText = `top:${top}px;left:${left}px;`;
}
});
element.addEventListener("touchend", () => {
move = false;
});
},
setCusorPosition(element, x, y, type) {
// 一定要先隐藏,不然选中的不是文字,是这个手柄
element.style.display = "none";
let offsetNode, offset;
if (document["caretRangeFromPoint"]) {
const pos = document["caretRangeFromPoint"](x, y);
if (!pos) {
return;
}
offsetNode = pos.startContainer;
offset = pos.startOffset;
console.log(pos, 333);
}
if (offsetNode.nodeType === Node.TEXT_NODE) {
const range = this.range;
const textNode = offsetNode;
if (type == "before") {
if (offset > range.endOffset) {
range.setStart(textNode, range.endOffset);
range.setEnd(textNode, offset);
} else if (offset == range.endOffset) {
range.setStart(textNode, offset - 1);
} else {
range.setStart(textNode, offset);
}
} else {
if (offset < range.startOffset) {
range.setStart(textNode, offset);
range.setEnd(textNode, range.startOffset);
} else if (offset == range.startOffset) {
range.setEnd(textNode, offset + 1);
} else {
range.setEnd(textNode, offset);
}
}
const rangeRect = range.getBoundingClientRect();
this.showCursorAndPop(rangeRect);
range.detach();
element.style.display = "block";
}
},
remarkClick(e) {
e.preventDefault();
e.stopPropagation();
const This = this;
//点击搜索的一瞬间,失焦
// 第一步:获取当前鼠标框选的选区(Selection)以及当前选区的Range
const selectText = window.getSelection().toString()
? window.getSelection().toString()
: this.mobileSelectContent
? this.mobileSelectContent
: ""; // 获取当前window滑选的文字
console.log("点击搜索", selectText);
this.destroyDiv();
this.$emit("search", selectText);
},
},
};
<style>
.text_search_wrap {
background: #fff;
border-radius: 4px;
padding: 3px 8px;
color: #666;
font-size: 12px;
position: absolute;
box-shadow: 0 0 1px 1px rgba(162, 162, 162, 0.6);
transform: translate(-50%, -100%);
}
.achor {
width: 2px;
height: 14px;
background: blue;
position: absolute;
&::after {
content: "";
position: absolute;
top: 0;
width: 10px;
height: 10px;
border: 2px solid blue;
border-radius: 10px;
left: 50%;
transform: translate(-50%, -100%);
}
}
.after {
&::after {
top: auto;
bottom: 0;
border: 2px solid blue;
transform: translate(-50%, 100%);
}
}
</style>
原文地址:https://blog.csdn.net/sunmeng0109/article/details/141060809
尚未验证,因为突然想到类似需求,便搜索查看,仅作备注,后续进行验证