使文本“更多”可选

时间:2016-02-20 22:00:16

标签: javascript jquery html css

我在<p>标记中有文字:

<p>Hello world... and goodbye mind A B!</p>

如何增加文本可选区域?我知道我可以增加font-size,这会增加可选区域,但有更好的方法吗?

澄清这个问题。例如,在移动屏幕上,我发现难以突出显示像i这样的单个字母的单词,但如果点击检测位于更宽的区域,则选择它会容易得多。

怎么做?一个令人费解的谜题。

赏金信息

寻找有效的跨浏览器解决方案。在发布答案之前,请仔细阅读问题和评论以避免混淆。用户@mmm发布了一个非常接近的问题,但在他的方法中,<p>标签具有更广泛的命中检测(完美!),它会在点击时自动选择。我需要用户与<p>标记进行互动,就像我们使用基于普通文本的<p>标记一样...但是使用更大的命中检测器。

修改

进一步澄清。例如,对这个问题的评论选择范围很大:

enter image description here

您可以在下方找到此评论。将光标悬停在其上,直到光标变为cursor:text。这是默认的选择区域。

但我的目标是将其扩展到更大的区域,如下所示:

enter image description here

6 个答案:

答案 0 :(得分:11)

从我的测试中它可以在iphone以及ff和chrome上运行 - 如果有人可以在Android上测试我会感激反馈!

边界显然可以删除。

此代码使用此答案中的代码(SelectText()函数的一部分): Selecting text in an element (akin to highlighting with your mouse)

Fiddle

代码:

function extendSelection() {
    var extendBy = arguments.length <= 0 || arguments[0] === undefined ? 15 : arguments[0];

    var extended = document.getElementsByClassName('extendedSelection');
    [].slice.call(extended).forEach(function (v) {
        var bounds = v.getBoundingClientRect();
        var x = bounds.left;
        var r = textWidth(v.innerHTML, ''+ css(v, 'font-weight') +' ' + css(v, 'font-size') + ' ' + css(v, 'font-family') );
        var y = bounds.top;
        var w = bounds.width;
        var h = bounds.height;
        var element = document.createElement('div');
        element.style.position = 'absolute';
        element.style.height = h + extendBy + 'px';
        element.style.width = r + extendBy + 'px';
        element.style.left = x - extendBy / 2 + 'px';
        element.style.top = y - extendBy / 2 + 'px';
        element.style.border = '1px dotted black';
        document.body.appendChild(element);
        element.addEventListener('click', function (e) {
            SelectText(v);
        });
        element.addEventListener('touchend', function (e) {
            SelectText(v);
        });
    });
}

function css(element, property) {
    return window.getComputedStyle(element, null).getPropertyValue(property);
}

function textWidth(text, font) {
    var el = textWidth.canvas || (textWidth.canvas = document.createElement("canvas"));
    var draw = el.getContext("2d");
    draw.font = font;
    var m = draw.measureText(text);
    return m.width;
};

function SelectText(element) {
    var doc = document,
        text = element,
        range,
        selection;
    if (doc.body.createTextRange) {
        range = document.body.createTextRange();
        range.moveToElementText(text);
        range.select();
    } else if (window.getSelection) {
        selection = window.getSelection();
        range = document.createRange();
        range.selectNodeContents(text);
        selection.removeAllRanges();
        selection.addRange(range);
        selection.setSelectionRange(0, element.value.length)
    }
}

extendSelection();

答案 1 :(得分:2)

您可以像已经建议的那样在段落周围添加填充,但也可以使用相同值的负边距来防止它影响布局。

以下是DEMO(双击或长按灰色区域内的任何位置都应选择文字)

相关代码:

HTML:

<p>normal paragraph</p>
<hr />
<p class="fat-fingers">fat fingers paragraph</p>

CSS:

p {
    //resetting default browser styles for brevity
    //otherwise adjust negative margin value so it's == default margin - padding
    margin: 0;
}

.fat-fingers {
    padding: 10px;
    margin: -10px;
}

注意:我没有测试两个区域重叠的情况,但我认为堆叠顺序较高的一个区域获胜。

答案 2 :(得分:1)

我假设您拥有一个文本正文的场景,并且在该文本正文中是最终用户的一个相当重要或相关的信息,您希望他们能够轻松突出显示复制信息。

如果没有找到其他解决方案,这将被视为最后一个选项,

<p class="surroundingText"> BLAH BLAH BLAH  <span class="importantText"> This is the information you would like users to be able to highlight </span> BLAH BLAH BLAH BLAH ETC ETC ETC </p>

如果你将文本包装在单独的段落标签中并给它们一个类,那么在CSS中设置以下内容:

.surroundingText {

  -webkit-user-select: none;  /* Chrome all / Safari all */
  -moz-user-select: none;     /* Firefox all */
  -ms-user-select: none;      /* IE 10+ */
  user-select: none;         
}

.importantText {

        -webkit-user-select: all;  /* Chrome all / Safari all */
      -moz-user-select: all;     /* Firefox all */
      -ms-user-select: all;      /* IE 10+ */
      user-select: all;
    }

因此最终结果是只能选择span标签之间的文本。

答案 3 :(得分:0)

夸大效果,但是:

#include <stdio.h>
#include <string.h>
#include "textmanipulation.h"

static int getLength(const char *str) {
    int i = 0;
    int count = 1;
    while (str[i+1] != '\0') {
        count++;
        i++;
    }
    return count;
}

void remove_spaces(const char *source, int *status, char *result) {
    int first = 0;
    int last;
    int i;
    int length = getLength(source);
    if (source[0] == '\0' || source == NULL) {
        *status = FAILURE;
    } else {
        printf("LENGTH: %d\n", length);
        *status = SUCCESS;
        for (i = 0; i < length; i++) {
            if (source[i] != ' ') {
                first = i;
                break;
            }
        }

        last = first;
        printf("FIRST: %d\n" , first);
        for (i = first; i < length; i++) {
            if (source[i] == ' ') {
                last = i - 1;
                break;
            }
        }
        for (i = first; i <= last; i++) {
            result[i] = source[i];
        }
    }
}

答案 4 :(得分:0)

一种方法是增加line-height元素的<p>。在移动设备上,您可以更好地选择文本片段,因为线条之间的间距较大。如果您在em中定义值,则间距始终相对于font-size

第二种方法是将文本元素的word-spacing增加到仍然可接受的水平。我建议最大值为0.2em

HTML:

<p class="extendedSelection">Extended Selection</p>

CSS:

p.extendedSelection {
   line-height: 2em;
   word-spacing: 0.2em;
}

JSFiddle here

如果这两种方法不够好,你当然可以为每个单词创建一个绝对定位元素,它覆盖文本元素,但不透明度为0.该元素内部的文本应该等于文本后面但是有更大的字体大小。这种方法有几个缺点:您需要计算每个单词的位置并复制文本内容。这只是一个相当大的计算效果。

答案 5 :(得分:0)

如果您可以获得额外的点击次数,可以考虑采用以下方法

  1. 用户需要先点击他/她想要选择的文本周围的某处。
  2. 根据点击位置,我们找到被点击的单词
  3. 现在我们接受上一个单词,当前(点击)单词和下一个单词,并在H1中的弹出窗口中显示它(或任何其他方式显示更大的区域以使其更可选)
  4. 当用户从此弹出窗口中选择文本时,我们关闭弹出窗口并在用户单击的原始元素中选择适当的文本
  5. 以下是我的尝试: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 = $('<span>')
                        .appendTo('body')
                        .addClass('expanded')
                        .css({
                          position: "absolute",
                          left: clicked[3].left,
                          top: clicked[3].top,
                          //width: "100px",
                          //height: "100px"
                        })
                        .append($("<h1>").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.left<x && r.right>x && r.top<y && r.bottom>y)
                {            
                    return r;
                }
            }
            return false;
        }
        return null;
    }
    

    注意:

    1. 可以改进弹出窗口的位置以满足您的需求。我专注于获取文本选择的代码,并且没有微调弹出位置逻辑。
    2. 由于时间有限,我只能在PC上的FF和Android上的Chrome上进行测试。
    3. 这是我第一次使用触摸事件,所以我不确定我是否以最佳方式使用它们。任何批评,建议都是最受欢迎的。
    4. 积分

      1. 此代码基于@mmm开头的想法,使用虚拟元素显示额外的区域供选择
      2. 我使用了@TeoDragovic共享的代码@ https://jsfiddle.net/abrady0/ggr5mu7o/的修改版本
      3. 让我知道你的想法

相关问题