2017-07-26 99 views
5

我有这个函数来获取当你点击文本时光标的位置,它只适用于等宽字符,这很好,但它显然不适用于字符比中国人或日本人更宽。如何在javascript中点击文本时获取字符位置

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库,但它不工作很正确(更广泛的字符和1的正常字母回报2)的功能,这里是演示代码:

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 = $('<span>&nbsp;</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) + '<span>' + 
 
      command[pos] + '</span>' + 
 
      command.substring(pos+1)); 
 
});
span { 
 
    color: red; 
 
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 
<script src="https://rawgit.com/jcubic/leash/master/lib/wcwidth.js"></script> 
 
<pre></pre>

,因为我有文字分成3个跨度之前,光标,光标后,我无法用跨度为每个字母。而且我也有可能在或不在容器中的样式的跨度。

任何帮助如何解决这个功能表示赞赏。

SOLUTION

这是基于@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; 
     } 
    } 

UPDATE:没有与点击,如果你对上半年单击它得到正确的选择的字符,但是当你的问题点击另一半选择下一个字符所以我最终以每个元素的方式处理一个字符。

+1

有什么用例呢? – charlietfl

+1

你在看多少个角色?将每个包装在自己的范围内是否可行? –

+0

@charlietfl我需要这个jQuery终端来移动光标点击。我已经有这个工作,但它不适用于更广泛的字符。 – jcubic

回答

0

第三次尝试。在那里填充一个管道字符来伪装成一个光标。

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; 
 
    }); 
 
}); 
 
});
<div class="charPosition">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</div>

+0

downvote的任何解释? – Will

+0

这不是我,也许是因为你已经添加了2个答案。 – jcubic

+0

你知道'focusOffset'的跨浏览器解决方案吗? – jcubic

3

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); 
 
     }); 
 
    }); 
 
});
<div class="charPosition">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</div>

+0

感谢您的解决方案,但我不能使用跨度,因为我可能跨越了我的容器中的样式,并且我将文本分为两部分,按光标划分,对不起没有提及。 – jcubic

相关问题