JS获取鼠标光标位置并在光标位置添加内容

这篇博客介绍了如何在React应用中获取鼠标光标位置,并实现点击按钮后在指定位置插入文本的功能。利用contenteditable属性、window.getSelection()和createRange()方法,结合React的状态管理和DOM操作,实现了编辑框的动态插入内容。文章详细讲解了每个关键步骤,并提供了完整的代码示例,包括HTML、CSS和JS部分。

目标需求

  • 获取鼠标光标位置,然后点击按钮或其他事件,在鼠标光标的位置插入需要的文字等

准备工具

  • contenteditable:contenteditable属性指定元素内容是否可编辑。
  • window.getSelection(): 获取鼠标光标位置
  • window.getSelection().getRangeAt(params):getRangeAt把鼠标光标在的位置分成前后两端,getRangeAt得到的是一个数组,params通常是数组的下表,我们这里写0
  • createRange():返回新创建的Range对象,可以用来表示文档的一个区域或与该文档相关的 DocumentFragment 对象。

HTML

react写法

<div id="contentTable" className={styles['infoBox']} placeholder='请输入内容' contentEditable suppressContentEditableWarning onblur={this.blurDiv} />

注意⚠️

  • 当不写suppressContentEditableWarning这个属性的时候,有可能会抛出警告:Warning: A component is contentEditable and contains children managed by React.

CSS

如果我们想给我div加一个placeholder的话,如下:

    .infoBox {
        width: 100%;
        height: 115px;
        padding: 16px;
        line-height: 1;
        outline: none;
        &:empty::before {
            content: attr(placeholder);
            color: #808080;
        }
    }

JS

    // 光标从id为content的div标签失焦的触发blurDiv
	blurDiv = () => {
		const positionObj = window.getSelection().getRangeAt(0)
		// setState是react自带的api,这里把positionObj存起来,其他方法用的到
		this.setState({ positionObj })
	}
	// 插入动作
	modalOk = () => {
		const { item } = this.state
		// item为要插入的相关信息
		this.insertFun(item)
	}
	// 插入某个位置
	insertFun = item => {
		const { positionObj } = this.state
		// contentWrap是拿到上面👆div的dom,react如果拿不到真实dom的话,可以用ref或者useRef获取真实的dom
		const contentWrap = document.getElementById('contentTable')
		const range = document.createRange()
		if (contentWrap.childNodes.length == 0) {
			contentWrap.appendChild(this.handleCreateContent(item))
		} else {
		// 这里判断positionObj是否为undefined,是因为第一添加的时候是undefined,第一次添加的时候直接insertNode到id为contentTable这个父节点
			if (positionObj == undefined) {
				range.setStart(contentWrap.childNodes[0], 0)
				range.setEnd(contentWrap.childNodes[0], 0)
				range.insertNode(this.handleCreateContent(item))
				return
			}
			range.setStart(positionObj.startContainer, positionObj.startOffset)
			range.setEnd(positionObj.startContainer, positionObj.startOffset)
			range.insertNode(this.handleCreateContent(item))
		}
	}
	// 处理要插入什么内容
	handleCreateContent = item => {
		const { dropMenuType, name, pagePath } = item
			const width = this.getTextWidth(`{${name}}`)
			const input = document.createElement('input')
			input.setAttribute('class', 'input')
			input.setAttribute('value', `{${name}}`)
			input.setAttribute('disabled', false)
			input.style.width = `${width + 5}px`
			input.style.border = 'none'
			return input
	}
	// 获取文字的宽度,因为input的宽度不能用width:fit-content自适应,并且这里用input标签的原因是因为想把要插入的内容当作一个整体(光标选不中input里面的内容,只能选input的前后)
	getTextWidth = str => {
		let dom = document.createElement('span')
		dom.setAttribute('id', 'textWidth')
		dom.style.display = 'inline-block'
		dom.textContent = str
		document.body.appendChild(dom)
		const w = document.getElementById('textWidth').clientWidth
		document.body.removeChild(document.getElementById('textWidth'))
		return w
	}
	// 最后,我们插入完,获取id为contentTable里面的内容
	getContent = () => {
		 const contentWrap = document.getElementById("contentTable");
		 // contentWrap.childNodes(数组的格式)里面就是我们要的数据,我们处理一下就可以了
		 console.log(contentWrap.childNodes)
	}
<think>好的,我现在需要回答用户关于如何在Vue中获取鼠标光标位置的问题。首先,我得回忆一下Vue的基本知识,特别是事件理相关的部分。用户可能想要在Vue组件中实时追踪鼠标位置,或者是在特定的元素上获取光标坐标。 首先,我应该考虑原生JavaScript中是如何获取鼠标位置的。通常,可以通过监听mousemove事件,然后从event对象中获取clientX和clientY属性。这点在Vue中应该也是适用的,因为Vue本身是基于JavaScript的,事件理机制应该类似。 接下来,我需要思考如何在Vue中绑定这些事件。Vue提供了v-on指令或者@符号来监听DOM事件。比如,在模板中可以直接用@mousemove来触发方法。这时候,方法里应该能接收到事件对象,进而提取出需要的坐标值。 然后,用户可能需要全局的鼠标位置,而不仅仅是某个元素内的。这时候,应该在组件的生命周期钩子中添加事件监听器,比如mounted的时候添加mousemove监听,在beforeDestroy或unmounted时移除,避免内存泄漏。需要注意的是,如果用户是在使用Vue 3的组合式API,可能会用setup函数和onMounted这样的钩子。 另外,有可能用户希望只在某个特定的元素上跟踪鼠标位置,比如一个div区域。这时候,应该将事件监听绑定到该元素上,而不是window或document对象。这样事件对象中的坐标会是相对于该元素的,可能需要使用offsetX和offsetY,或者根据元素的边界计算相对位置。 然后,我需要考虑示例代码的结构。应该展示两种常见情况:全局窗口内的鼠标位置和特定元素内的相对位置。对于全局情况,在组件挂载时添加事件监听,保存坐标到data或reactive变量中。对于元素内的情况,直接在模板中绑定事件,在方法中更新数据。 同时,需要注意Vue 2和Vue 3的语法差异。比如,Vue 2中使用data函数返回对象,而Vue 3可以使用ref或reactive。示例中可能需要分别说明,或者选择其中一个版本进行演示,但用户可能使用的是Vue 3,所以应该优先考虑组合式API的写法。 另外,事件对象的传递问题。在Vue中,如果不显式传递$event,方法可能无法直接获取事件对象。不过,当直接在模板中绑定事件理器时,如果没有参数,默认会传递event对象。这一点需要确认正确性,避免示例代码出现错误。 还有性能方面的考虑,频繁的mousemove事件可能会影响性能,尤其是在理复杂操作时。可能需要提及节流或防抖的技术,但用户的问题主要在于获取位置,所以可能不需要深入,但可以简单提到优化方法。 最后,总结一下步骤:绑定事件、获取事件对象中的坐标、更新响应式数据、在模板中显示数据。同时,注意清理事件监听器,避免潜在的问题。确保示例代码正确,语法符合Vue的规范,比如使用this访问data属性,或者在组合式API中使用.value。 可能需要检查一下是否需要使用clientX/Y,或者pageX/Y等其他属性,根据用户需求的不同解释这些属性的区别。比如,clientX是相对于视口的坐标,而pageX是相对于整个文档的。但一般情况下,clientX/Y更常用,尤其是在不涉及滚动的情况下。 总之,回答的结构应该是先介绍基本方法,然后分情况给出示例代码,解释每个步骤,提醒注意事项,比如清除事件监听和性能优化。确保用户能够根据示例正确实现功能,同时理解背后的原理。</think>在Vue中获取鼠标光标位置,可以通过监听`mousemove`事件提取事件对象中的坐标属性实现。以下是两种常见场景的解决方案: --- ### 一、全局窗口鼠标位置追踪 ```vue <template> <div> 鼠标当前位置:X: {{ x }}, Y: {{ y }} </div> </template> <script> // Vue 3 组合式API写法 import { ref, onMounted, onUnmounted } from 'vue' export default { setup() { const x = ref(0) const y = ref(0) const updatePosition = (event) => { x.value = event.clientX y.value = event.clientY } onMounted(() => { window.addEventListener('mousemove', updatePosition) }) onUnmounted(() => { window.removeEventListener('mousemove', updatePosition) }) return { x, y } } } </script> ``` --- ### 二、特定元素内相对位置 ```vue <template> <div @mousemove="handleMouseMove" style="width: 300px; height: 200px; border: 1px solid" > 元素内坐标:X: {{ localX }}, Y: {{ localY }} </div> </template> <script> // Vue 2 选项式API写法 export default { data() { return { localX: 0, localY: 0 } }, methods: { handleMouseMove(event) { this.localX = event.offsetX // 相对于元素的X坐标 this.localY = event.offsetY // 相对于元素的Y坐标 } } } </script> ``` --- ### 关键属性说明 1. **clientX/clientY** 相对于浏览器窗口可视区域的坐标(不包含滚动偏移) 2. **pageX/pageY** 相对于整个文档的坐标(包含滚动偏移) 3. **offsetX/offsetY** 相对于事件触发元素的坐标 --- ### 注意事项 1. **性能优化** 高频触发`mousemove`事件时,建议使用防抖(debounce): ```javascript import { debounce } from 'lodash' // 在setup()中 const updatePosition = debounce((event) => { x.value = event.clientX y.value = event.clientY }, 50) ``` 2. **事件解绑** 使用`window.addEventListener`后,务必在组件销毁时用`removeEventListener`解除绑定,避免内存泄漏 3. **移动端兼容** 如需支持触摸设备,需同时监听`touchmove`事件,通过`event.touches[0].clientX`获取坐标
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值