如何在javascript中单击文本时获取字符位置

我有这个function来点击文本时获取光标的位置,它只适用于等宽字符,这很好,但它显然不适用于像中文或日文更宽的字符。

function get_char_pos(point) { var prompt_len = self.find('.prompt').text().length; var size = get_char_size(); var width = size.width; var height = size.height; var offset = self.offset(); var col = Math.floor((point.x - offset.left) / width); var row = Math.floor((point.y - offset.top) / height); var lines = get_splited_command_line(command); var try_pos; if (row > 0 && lines.length > 1) { try_pos = col + lines.slice(0, row).reduce(function(sum, line) { return sum + line.length; }, 0); } else { try_pos = col - prompt_len; } // tabs are 4 spaces and newline don't show up in results var text = command.replace(/\t/g, '\x00\x00\x00\x00').replace(/\n/, ''); var before = text.slice(0, try_pos); var len = before.replace(/\x00{4}/g, '\t').replace(/\x00+/, '').length; return len > command.length ? command.length : len; } 

我试图使用wcwidth库创建一个函数(对于更宽的字符返回2,对于普通字母返回1)但它不能正常工作,这是带有demo的代码:

 var self = $('pre'); var offset = self.offset(); var command = 'チトシタテイトチトシイスチトシタテイトチトシイスチトシタテイトチトシイス\nfoo bar baz\nfoo bar baz\nチトシタテイトチトシイ'; self.html(command); function get_char_size() { var span = $(' ').appendTo(self); var rect = span[0].getBoundingClientRect(); span.remove(); return rect; } var length = wcwidth; // mock function get_splited_command_line(string) { return string.split('\n'); } function get_char_pos(point) { var size = get_char_size(); var width = size.width; var height = size.height; var offset = self.offset(); var col_count = Math.floor((point.x - offset.left) / width); var row = Math.floor((point.y - offset.top) / height); var lines = get_splited_command_line(command); var line = lines[row]; var col = 0; var i = col_count; while (i > 0) { i -= length(line[col]); col++; } var try_pos; if (row > 0 && lines.length > 1) { try_pos = col + lines.slice(0, row).reduce(function(sum, line) { return sum + length(line); }, 0); } else { try_pos = col; } // tabs are 4 spaces and newline don't show up in results var text = command.replace(/\t/g, '\x00\x00\x00\x00').replace(/\n/, ''); var before = text.slice(0, try_pos); var len = before.replace(/\x00{4}/g, '\t').replace(/\x00+/, '').length; var command_len = command.length; return len > command_len ? command_len : len; } self.click(function(e) { var pos = get_char_pos({ x: e.pageX, y: e.pageY }); self.html(command.substring(0, pos-1) + '' + command[pos] + '' + command.substring(pos+1)); }); 
 span { color: red; } 
   

我不能为每个字母使用跨度,因为我将文本拆分为3个跨度,光标和光标之后。 而且我也有可能在容器中或不在容器中的样式的跨度。

任何帮助如何修复此function表示赞赏。

解决方案

这是我基于@Will的代码,我在我的代码中使用了多个元素(由于某些原因,当你点击只有一个字符的元素时,chrome会出现问题,以防焦点不是更多文字长度):

  function get_focus_offset() { var sel; if ((sel = window.getSelection()) && (sel.focusNode !== null)) { return sel.focusOffset; } } function get_char_pos(e) { var focus = get_focus_offset(); if ($.isNumeric(focus)) { var node = $(e.target); // [role="presentation"] is my direct children that have // siblings that are other nodes with text var parent = node.closest('[role="presentation"]'); var len = node.text().length; focus = len === 1 ? 0 : Math.min(focus, len); return focus + parent.prevUntil('.prompt').text_length() + node.prevAll().text_length(); } else { return command.length; } } 

更新 :如果您单击正确选择的前半部分字符,则单击会出现问题,但是当您单击另一半时,它会选择下一个字符,因此我最终每个元素的方法都有一个字符。

第三次尝试。 在那里填充一个管道字符,假装是一个光标。

https://developer.mozilla.org/en-US/docs/Web/API/Selection

 window.addEventListener('DOMContentLoaded', () => { document.querySelectorAll('.charPosition').forEach(el => { let clean, cursor; el.addEventListener('click', e => { let position = window.getSelection().focusOffset; if (cursor && position > cursor) position--; if (clean) el['innerText'] = clean; let textnode = el.firstChild['splitText'](position); clean = textnode.wholeText; cursor = position; el.insertBefore(document.createTextNode('|'), textnode); el['innerText'] = textnode.wholeText; }); }); }); 
 
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 window.addEventListener('DOMContentLoaded', () => { document.querySelectorAll('.charPosition').forEach(el => { let characters = el['innerText'].split(''); el.innerHTML = ''; characters.forEach(char => { let span = document.createElement('span'); span.innerText = char; span.addEventListener('click', function () { let position = 0; let el = this; while (el.previousSibling !== null) { position++; el = el.previousSibling; } console.log(this.innerHTML + ':' + position); }); el.appendChild(span); }); }); }); 
 
Lorem ipsum dolor sit amet, consectetur adipiscing elit.