Подтвердить что ты не робот

Сделать текст "более" выбираемым

У меня есть текст в теге <p>:

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

Как увеличить область, в которой текст можно выбрать? Я знаю, что могу увеличить font-size, и это увеличит область, которую можно выбрать, но есть ли лучший способ?

Прояснить этот вопрос. Например, на мобильных экранах мне сложно выделить слова, которые являются одной буквой, например i, но если обнаружение попадания будет на более широкой области, было бы намного легче выбрать его.

Как это сделать? Мозг-дразнящая головоломка.

Информация о баунти

Ищете решение для перекрестного браузера. Пожалуйста, внимательно прочитайте вопрос и комментарии, прежде чем отправлять ответ, чтобы избежать путаницы. Пользователь @mmm опубликовал вопрос, который довольно близко, но в своем подходе, в то время как тег <p> имеет более широкое обнаружение (совершенное!), Он автоматически выбирает по щелчку. Мне нужно, чтобы пользователь взаимодействовал с тегом <p> так же, как и с обычными текстовыми тегами <p>... однако с большим детектором попадания.

ИЗМЕНИТЬ

Дальнейшие разъяснения. Например, область выбора для комментария к этому самому вопросу такова:

введите описание изображения здесь

Вы можете найти этот комментарий ниже. Наведите курсор на него, пока курсор не изменится на cursor:text. Это область выбора по умолчанию.

Но моя цель - распространить его на большую область, например:

введите описание изображения здесь

4b9b3361

Ответ 1

Из моего теста он работает на iphone, а также на ff и chrome - если кто-то может проверить на android, я буду благодарен за обратную связь!

Граница, очевидно, может быть удалена.

Этот код использует код из этого ответа (часть функции SelectText()): Выбор текста в элементе (сродни подсветке мышью)

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();

Ответ 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 == default margin - padding
    margin: 0;
}

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

note: Я не тестировал случай перекрытия двух областей, но я предполагаю, что выигрывает тот, у кого выше порядок стекирования.

Ответ 3

Я предполагаю сценарий, в котором у вас есть текст, и внутри этого текста текст является довольно важной или важной частью информации для конечного пользователя, и вы хотели бы, чтобы они могли легко выделить и скопировать Информация.

Это будет рассматриваться как последний вариант, если не найдено другого решения,

<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.

Ответ 4

Преувеличенный эффект, но как насчет:

<head>
    <style>   
    p {
        letter-spacing: 2px;
        line-height: 3em;
        word-spacing: 1.5em;
        vertical-align: middle;
    }
    </style>
</head>
<body >

    <p>this is a line.</p>
    <p>this is a line.</p>
    <p>this is a line.</p>
    <p>this is a line.</p>
    <p>this is a line.</p>
    <p>this is a line.</p>
    <p>this is a line.</p>
</body>

Ответ 5

Один из подходов состоит в увеличении 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 здесь

Если эти два подхода недостаточно хороши, вы можете, конечно, создать абсолютный позиционный элемент для каждого слова, который накладывает текстовый элемент, но имеет непрозрачность 0. Текст внутри этого элемента должен быть равен тексту сзади, но с большим размером шрифта. Этот подход имеет несколько недостатков: вам нужно рассчитать позиции каждого слова и дублировать текстовое содержимое. Это довольно большой расчет для небольшого эффекта.

Ответ 6

Вот один из подходов, который можно рассмотреть, если вы в порядке, чтобы получить дополнительные клики

  • пользователь должен сначала щелкнуть где-нибудь вокруг текста, который он/она хочет выбрать.
  • в зависимости от позиции щелчка мы узнаем слово, которое было нажато
  • теперь мы берем предыдущее слово, текущее (щелкнутое) слово и следующее слово и показываем его во всплывающем окне внутри H1 (или любое другое средство, чтобы показать большую область, чтобы сделать ее более доступной).
  • когда пользователь выбирает текст из этого всплывающего окна, мы закрываем всплывающее окно и выбираем соответствующий текст в исходном элементе, который пользователь нажал

Вот моя попытка: 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;
}

Примечание:

  • Позиционирование всплывающего окна может быть улучшено в соответствии с вашими потребностями. Я сосредоточился на том, чтобы получить код для выбора текста и не настраивать логику всплывающего окна.
  • С ограниченным временем я могу проверить его только в FF на ПК и Chrome на Android.
  • Впервые я использовал события касания, поэтому я не уверен, что использовал их наилучшим образом. Любая критика, предложения приветствуются.

Кредиты

  • Этот код основан на идее, с которой начал работать @mmm, использовать фиктивный элемент для отображения дополнительной области для выбора
  • Я использовал измененную версию кода @https://jsfiddle.net/abrady0/ggr5mu7o/, совместно используемую @TeoDragovic

Сообщите мне свои мысли