Make text “more” selectable

前端 未结 6 1954
心在旅途
心在旅途 2021-02-01 17:48

I have text in a

tag:

Hello world... and goodbye mind A B!

How do I increase the area in which the te

6条回答
  •  梦如初夏
    2021-02-01 18:19

    Here is one approach that can be considered if you are ok to have some extra clicks

    1. user needs to first click somewhere around the text that s/he wants to select.
    2. based on the click position we find out the word that was clicked
    3. now we take the previous word, current (clicked) word and next word and show it in a popup within H1 (or any other means to show bigger area to make it more selectable)
    4. when user selects the text from this popup, we close the popup and select appropriate text in the original element that user had clicked

    Here is my attempt: https://jsfiddle.net/vnathalye/rtw5bvLx/6/

    $('.expandable').click(function(e){
        var clicked = findClickedWord(e.target.childNodes[0], e.clientX, e.clientY);
      if(clicked){
        var $expanded = $('')
                        .appendTo('body')
                        .addClass('expanded')
                        .css({
                          position: "absolute",
                          left: clicked[3].left,
                          top: clicked[3].top,
                          //width: "100px",
                          //height: "100px"
                        })
                        .append($("

    ").text(clicked[0])); var data = {originalElem: e.target.childNodes[0], index: clicked[1], starts: clicked[2]}; $expanded.data("parentData", data); $expanded.on('mouseup', selectionChanged); $expanded.on('touchend touchcancel', selectionChanged); //alert(JSON.stringify(clicked)); } }); function selectionChanged(e){ try { var $expanded = $(e.target); var data = $expanded.parents(".expanded").data("parentData"); var selection = window.getSelection(); if(selection.rangeCount){ var range1 = selection.getRangeAt(0); //alert(range1.startOffset + ":" + range1.endOffset); var range2 = document.createRange(); var originalOffset = data.index>0? data.starts[data.index-1] : data.starts[0]; range2.setStart(data.originalElem, originalOffset + range1.startOffset); range2.setEnd(data.originalElem, originalOffset + range1.endOffset); selection.removeAllRanges(); selection.addRange(range2); } } catch(err){ alert(err); } $expanded.parents(".expanded").remove(); } function findClickedWord(parentElt, x, y) { if (parentElt.nodeName !== '#text') { console.log('didn\'t click on text node'); return null; } var range = document.createRange(); var words = parentElt.textContent.split(' '); var start = 0; var end = 0; var starts=[]; var ends=[]; for (var i = 0; i < words.length; i++) { var word = words[i]; end = start+word.length; starts.push(start); ends.push(end); range.setStart(parentElt, start); range.setEnd(parentElt, end); // not getBoundingClientRect as word could wrap var rects = range.getClientRects(); var clickedRect = isClickInRects(rects); if (clickedRect) { var str = (i==0)? word : words[i-1] + " " + word; if(i!=words.length-1) str += " " + words[i+1]; return [str, i, starts, clickedRect]; } start = end + 1; } function isClickInRects(rects) { for (var i = 0; i < rects.length; ++i) { var r = rects[i] if (r.leftx && r.topy) { return r; } } return false; } return null; }

    Note:

    1. Positioning of the popup can be improved to suit your needs. I've focused on getting the code for text selection working and have not fine-tuned popup position logic.
    2. With limited time I could test it only in FF on PC and Chrome on Android.
    3. Its the first time I've used touch events, so I'm not sure if I've used them in best possible way. Any criticism, suggestions are most welcome.

    Credits

    1. This code is based on the idea that @mmm started with, to use a dummy element to show the extra area for selection
    2. I've used modified version of the code @ https://jsfiddle.net/abrady0/ggr5mu7o/ shared by @TeoDragovic

    Let me know your thoughts

提交回复
热议问题