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

Как я могу выделить слово/термин быстрее и умнее?

У меня есть текст:

<p class="drag">Hello world, Attack on Titan season two!</p>

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

Я хочу, чтобы этот процесс был быстрее. Например, если пользователь начинает выделять At, он должен автоматически выделять остальную часть слова Attack. Таким образом, пустое пространство является разделителем.

Я знаю, что это возможно, разделив слова на divs, но я надеюсь на решение с чистым текстом внутри одного тега <p>.

4b9b3361

Ответ 1

Вы можете сделать это с помощью чистого JS, используя объекты Range и selectionRange.

HTML:

<div id="selectable">
  <p>Hello world, <b>Attack on Titan</b> season two!</p>
  <p>Another paragraph with sample text.</p>
</div>
<div id="notSelectable">
  <p>The selection will behave normally on this div.</p>
</div>

JS:

(function(el){
    el.addEventListener('mouseup',function(evt){
        if (document.createRange) { // Works on all browsers, including IE 9+
            var selected = window.getSelection();
            /* if(selected.toString().length){ */
                var d = document,
                    nA = selected.anchorNode,
                    oA = selected.anchorOffset,
                    nF = selected.focusNode,
                    oF = selected.focusOffset,
                    range = d.createRange();

                range.setStart(nA,oA);
                range.setEnd(nF,oF);

                // Check if direction of selection is right to left
                if(range.startContainer !== nA || (nA === nF && oF < oA)){
                    range.setStart(nF,oF);
                    range.setEnd(nA,oA);
                }

                // Extend range to the next space or end of node
                while(range.endOffset < range.endContainer.textContent.length && !/\s$/.test(range.toString())){
                    range.setEnd(range.endContainer, range.endOffset + 1);
                }
                // Extend range to the previous space or start of node
                while(range.startOffset > 0 && !/^\s/.test(range.toString())){
                    range.setStart(range.startContainer, range.startOffset - 1);
                }

                // Remove spaces
                if(/\s$/.test(range.toString()) && range.endOffset > 0)
                    range.setEnd(range.endContainer, range.endOffset - 1);
                if(/^\s/.test(range.toString()))
                    range.setStart(range.startContainer, range.startOffset + 1);

                // Assign range to selection
                selected.addRange(range);
            /* } */
        } else { 
           // Fallback for Internet Explorer 8 and earlier
           // (if you think it still is worth the effort of course)
        }

        // Stop Moz user select
        el.style.MozUserSelect = '-moz-none';
    });

    /* This part is added to eliminate a FF specific dragging behavior */
    el.addEventListener('mousedown',function(){
        if (window.getSelection) {  // Works on all browsers, including IE 9+
           var selection = window.getSelection ();
           selection.collapse (selection.anchorNode, selection.anchorOffset);
        } else {
           // Fallback for Internet Explorer 8 and earlier
           // (if you think it still is worth the effort of course)
        }

        // Add Moz user select back
        el.style.MozUserSelect = 'text';
    });
})(document.getElementById('selectable'));

Пожалуйста, проверьте рабочий пример здесь.

ОБНОВЛЕНИЕ:

  • Выбор всего слова при добавлении клика
  • Исправление для конкретного перетаскивания Firefox добавленное поведение.

Обновлен JSFiddle здесь.

Ответ 2

Используя node.textContent, мы можем найти пробелы и перевести наш выбор на целые слова без необходимости создания новых элементов.

В основном для моего потенциального будущего использования я написал это довольно модульное, он также не требует мыши на наблюдаемом элементе, может иметь дело с коллекцией элементов, а также делает изменения выбора, если пользователь делает свой выбор, используя свои клавиатура.

var WordJumpSelection = (function() {
    var watchList = [];
    var WordJumpSelection = {
        stopWatching: function(elem) {
            var wlIdx = watchList.indexOf(elem);
            if(wlIdx > -1) watchList.splice(wlIdx,1);
        },
        watch: function(elem) {
            var elems = Array.prototype.slice.call(typeof elem.length === "number" ? elem : arguments);
            if(watchList.length === 0)
            {
                WordJumpSelection.init();
            }
            elems.forEach(function(elem) {
                if(watchList.indexOf(elem) === -1)
                {
                    watchList.push(elem);
                }
            });
        },
        init: function() {
            function handleSelectionChange() {
                if(watchList.length === 0) return;
                var selection = window.getSelection();
                var selDir = getSelectionDir(selection);
                var startNode,endNode,startPos,endPos;
                if(selDir === 1)
                {
                    startNode = selection.anchorNode;
                    endNode = selection.focusNode;
                    startPos = selection.anchorOffset;
                    endPos = selection.focusOffset;
                }
                else
                {
                    startNode = selection.focusNode;
                    endNode = selection.anchorNode;
                    startPos = selection.focusOffset;
                    endPos = selection.anchorOffset;
                }
                var rangeStart = textNodeIsWatched(startNode) ? roundSelectionIndex(startNode,0,startPos) : startPos-1;
                var rangeEnd = textNodeIsWatched(endNode) ? roundSelectionIndex(endNode,1,endPos) : endPos;
                var r = document.createRange();
                r.setStart(startNode,rangeStart+1)
                r.setEnd(endNode,rangeEnd)
                selection.removeAllRanges();
                selection.addRange(r);
            }
            document.documentElement.addEventListener('mouseup', handleSelectionChange);
            document.documentElement.addEventListener('keyup', function(e) {
                if(e.keyCode === 16)
                {
                    handleSelectionChange();
                }
            });
            WordJumpSelection.init = function(){};
        }
    };
    return WordJumpSelection;

    function getSelectionDir(sel) {
        var range = document.createRange();
        range.setStart(sel.anchorNode,sel.anchorOffset);
        range.setEnd(sel.focusNode,sel.focusOffset);
        if(range.startContainer !== sel.anchorNode || (sel.anchorNode === sel.focusNode && sel.focusOffset < sel.anchorOffset)) return -1;
        else return 1;
    }
    function roundSelectionIndex(textNode,nodeId,idx) {
        var isStart = nodeId === 0;
        var contents = textNode.textContent;
        var nearestSpaceIdx = -1;
        if(isStart)
        {
            nearestSpaceIdx = contents.lastIndexOf(' ',idx);
            if(nearestSpaceIdx === -1) nearestSpaceIdx = -1;
        }
        else
        {
            nearestSpaceIdx = contents.indexOf(' ',idx);
            if(nearestSpaceIdx === -1) nearestSpaceIdx = contents.length;
        }
        return nearestSpaceIdx;
    }
    function textNodeIsWatched(textNode) {
        return watchList.indexOf(textNode.parentElement) > -1;
    }
})();

Пример jsFiddle

Мне еще предстоит проверить, как это работает на мобильных устройствах, и пока не работает, но это может быть хорошим началом.

Обновление: теперь выбирает слово одним щелчком мыши

Ответ 3

Концепция

Чтобы выбрать каждое слово, сначала нужно иметь в виду следующее:

  • textNode - это единственное жало, содержащее все слова, вы не сможет выбрать каждое слово, так как это не DOM node.

  • В браузере нет определенного события, когда вы перетаскиваете и   выберите "слово". Однако, когда вы перетаскиваете и выбираете, есть 2 события   увольнение: mouseover запускается при перемещении мыши,   click запускается при отпускании кнопки мыши. (Это   true даже в сенсорной панели Mac).

  • Существуют разные варианты реализации на "highlight", когда вы выбираете   слово.

Действия

Основываясь на концепциях, вы должны сделать следующие шаги последовательно для достижения своей цели:

  • Получить слова в параграфе и обернуть их тегом (например, <span>) для выбора DOM
  • Когда событие click запускается (что указывает на завершение выбора), выделите слово, которое вы только что выбрали.

Реализация будет чем-то понравится (с jQuery). И вы можете увидеть живое демо здесь:

$(function() {

  // 1. When mouseover the paragraph, wrapped each word with <span>
  $('p').one('mouseover', function(event) {
    $('p').html(function(index, text) {
      var wordsArray = text.split(' ');

      var wrappedArray = wordsArray.map(function(val, index) {
        val = '<span class="chunk-' + index + '">' + val + '</span>';
        return val;
      });

      var wrappedString = wrappedArray.join(' ');

      // 2. Replace the paragraph with wrapped text
      $(this).html(wrappedString);

      // 3. When the word is select, highlight the word
      $(this).children('span').on('click', function() {
        var selector = '.' + $(this).attr('class');
        SelectText(selector);
      });
    });
  });
});


function SelectText(element) {
  var doc = document,
    text = doc.querySelector(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);
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Autem amet suscipit incidunt placeat dicta iure, perspiciatis libero nobis dolore, temporibus et! Quae fugiat necessitatibus ut, molestias aut. Sequi rerum earum facilis voluptates ratione architecto
  officia quod aut unde voluptas? Dignissimos ducimus exercitationem perspiciatis nam numquam minima accusamus quod necessitatibus amet illo vel vero placeat voluptate eos iste ratione veniam quisquam atque non voluptatum sint hic sed, suscipit. Doloremque
  officiis rerum sunt delectus unde odit eos quod earum aspernatur, tempora neque modi tempore minima maiores fuga eaque dolore quos minus veritatis aliquid, vel suscipit dolores. Voluptatem eius obcaecati, laborum ipsa a!</p>

Ответ 4

Этот текст представляет собой текст node, а текстовые узлы просто не запускают большинство событий. Но они могут запускать события мутации DOM, например DOMCharacterDataModified, который используется для обнаружения изменения текста текста node:

var textNode = document.getElementsByClassName("drag")[0].firstChild;

textNode.addEventListener("DOMCharacterDataModified", function(e) {
    console.log("Text changed from '" + e.prevValue + "' to '" + evt.newValue +"'");
}, false);

Однако текст в <p class="drag">Hello world, Attack on Titan season two!</p> - это один текст node, и вам нужно, чтобы каждое слово было отдельным node.

Единственное решение, которое я вижу, - это положить каждое слово в тег span. Вы не можете сделать это с помощью чистого текста.

Изменить

Вот пример, как это сделать с тегами span (я использую jQuery здесь, чтобы уменьшить количество кода, это не обязательно):

$(function() {
    $('.drag').on('click', 'span', function() {
        var range;
        if (document.selection) {
            range = document.body.createTextRange();
            range.moveToElementText($(this)[0]);
            range.select();
        } 
        else if (window.getSelection) {
            range = document.createRange();
            range.selectNode($(this)[0]);
            window.getSelection().addRange(range);
        }
    });  
});

Вот пример на JS Bin

Update

Я отредактировал фрагмент кода , поэтому выбор ведет себя так же, как вы его спросили (в настоящее время он работает только для выбора слева направо, а не для обратного выбора):

$(function(){

  var range = document.createRange();
  var selectionMode = false;

  $(document).on('mouseup', function() {
    selectionMode = false;
  })
  .on('mousedown', '.drag', function(e) {
    selectionMode = true;
  })
  .on('dragstart', '.drag span', function(e) {
    return false;
  });

  $('.drag').on('mousedown', 'span', function() {
    range.setStartBefore($(this)[0]);
    range.setEndAfter($(this)[0]);
    window.getSelection().addRange(range);
  })
  .on('mousemove', 'span', function() {
    if (!selectionMode) {
      return;
    }
    range.setEndAfter($(this)[0]);
    window.getSelection().addRange(range);
  })
  .on('mouseup', 'span', function() {
    setTimeout(function(){
      window.getSelection().addRange(range);
    }, 1);
  });  

});

Подробнее о API диапазона HTML вы можете прочитать здесь: https://developer.mozilla.org/en-US/docs/Web/API/Range

Ответ 5

Таким образом, вам придется иметь дело с текстовыми диапазонами и т.д. Я имел дело с этим, и это очень болезненно, особенно если у вас есть содержимое DOM, например:

<p>New season of <span class="redtext">Attack on Titan!</span></p>

то есть. текстовые узлы, смешанные с другими элементами DOM, такие как диапазон в этом случае. Имея это в виду, я хотел бы настоятельно рекомендовать библиотеку rangy.js: https://github.com/timdown/rangy

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

Ответ 6

Вы не можете устанавливать события в текст, но можете устанавливать события в Html Elements. Поместите каждое слово внутри элемента div и добавьте событие onmouseover, которое изменит div на новое выделенное состояние с помощью css.

Шаги:

  • Используйте split, чтобы получить слова в массиве.
  • Итерировать слова и поместить их в div.
  • Итерации div и установите для него событие, которое изменит div на класс css.highlight.

Что это.