【问题标题】:How to extend selected text to include whole words如何扩展所选文本以包含整个单词
【发布时间】:2017-06-30 21:59:19
【问题描述】:

假设我有一个句子:“这是一个测试句子。”我希望用户能够选择文本的小节,但没有标点符号或不完整的单词。

所以“测试句”。应该变成“测试句” 而“est sentence”也应该变成“test sentence”

这是我当前的代码:

var getSelectedText = function() {
    var text = "";
    if (window.getSelection) {
        text = window.getSelection().toString().trim();
    } else if (document.selection && document.selection.type != "Control") {
        text = document.selection.createRange().text;
    }
    return text;
}

fyi:jQuery 代码没问题。

为 Bender 编辑:

好的,这就差不多了。我有超过 50k 的句子,用户的选择是可变的,所以我必须做这样的事情:

var selection = getSelectedText();
var exp = new RegExp("\\w*" + selection + "\\w+");
text.match(exp);

但是,如果用户确实选择了更有可能的“测试句子”,这将不匹配。

【问题讨论】:

  • 我会使用正则表达式来匹配整个单词。这是一个基本示例:regex101.com/r/xW0mR8/1 您必须对其进行增强才能使其在任何选择情况下都能正常工作
  • @BeNdErR 聪明! :)
  • @BeNdErR 请看我的编辑。
  • 要匹配“测试句”,你应该在两边都使用“\\w*”,像这样:new RegExp("\\w*" + selection + "\\w*");(基于BeNdErR的代码:regex101.com/r/xW0mR8/2
  • @AlvaroMontoro 谢谢阿尔瓦罗。

标签: javascript jquery


【解决方案1】:

有趣的挑战。

下面的代码将所选内容包装在类selected的跨度中。

它抓取新元素的previousSiblingnextSiblingnodeValue - 通过拆分非单词字符、弹出(previousSibling)或移位(nextSibling)来获取适当的文本。

然后它会删除 selected 跨度(同时保留其内容)。这必须在超时内完成,以便元素有时间添加到 DOM。

此时,我们剩下三个相邻的文本节点。代码通过在文档正文上调用normalize() 来加入它们。

$(document).mouseup(function() {
  alert(getSelectedText());
});

var getSelectedText = function() {
  var el= document.createElement('span'),
      sel= window.getSelection().getRangeAt(0),
      prev= '',
      next= '';
  
  el.className= 'selected';
  sel.surroundContents(el);
  if(!sel.toString().match(/^\W/)) {
    prev= el.previousSibling.nodeValue;
    if(prev.match(/\W$/)) {
      prev= '';
    }
    else {
      prev= prev.split(/\W/).pop();
    }
  }
  if(!sel.toString().match(/\W$/)) {
    next= el.nextSibling.nodeValue;
    if(next.match(/^\W/)) {
      next= '';
    }
    else {
      next= next.split(/\W/).shift();
    }
  }
  setTimeout(function() {
    $('.selected').contents().unwrap()
    $('.selected').remove();
    document.body.normalize();
  });
  return prev+sel.toString()+next;
}
.selected {
  color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
This is a test sentence.  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

【讨论】:

    【解决方案2】:

    我设法通过为每个单词添加跨度来使其工作。这个想法是您将不能选择少于一个跨度/单词,这样将选择整个跨度。 这个答案不依赖于 jQuery

    function customSelect(target, onSelect) {
      var oldHTML = target.innerHTML;
      
      // this is the regex that wraps the words with spans:
      target.innerHTML = oldHTML.replace(/[\d\w']+[\s,.]*/g, '<span>$&</span>');
      var spans = target.querySelectorAll('span');
      
      // I used a basic blue/white style, but you can change it in the CSS
      // using the ".text-selected" selector 
      var alreadySelected = [];
      var setSpanOn = function(span) {
        alreadySelected.push(span);
        span.className = 'text-selected';
      };
      var setSpanOff = function(span) {
        span.className = '';
      };
      
      // here starts the logic
      var isSelecting = false;
      for (var i=0, l=spans.length; i<l; i++) {
        (function span_handlers(span, pos) {
        
          // when the user starts holding the mouse button
          span.onmousedown = function() {
            // deselect previous selection, if any:
            alreadySelected.splice(0).forEach(setSpanOff);
            
            // and enable selection:
            isSelecting = true;
            span.onmouseenter();
          };
          
          // the main logic, we check if we need to set or not this span as selected:
          span.onmouseenter = function() {
            if (!isSelecting)
              return;
            
            // if already selected
            var j = alreadySelected.indexOf(span);
            if (j >= 0) {
              // then deselect the spans that were selected after this span
              alreadySelected.splice(j+1).forEach(setSpanOff);
            }
            else {
              // else if is not the first, check if the user selected another word 
              // one line down or up. This is done by checking the indexes:
              if (alreadySelected.length) {
                var last = alreadySelected[alreadySelected.length-1];
                var posLast = [].indexOf.call(spans, last);
                var typeSibling = pos > posLast ? 'nextSibling' : 'previousSibling';
                while (1) {
                  last = last[typeSibling];
                  if (last !== span)
                    setSpanOn(last);
                  else break;
                }
              }
              setSpanOn(span);
            }
          };
          
          // when the user hold up the mouse button:
          span.onmouseup = function() {
            isSelecting = false;
            
            //  call the onSelect function passing the selected spans content:
            if (typeof onSelect === 'function') {
              var spansSelected = target.querySelectorAll('.text-selected');
              var text = [].map.call(spansSelected, function(span) {
                return span.textContent || '';
              }).join('').trim();
              onSelect(text);
            }
          };
        })(spans[i], i);
      }
    };
    
    // Usage:
    var element = document.getElementById('target');
    var onSelect = function(text) {
      console.log(text);
    };
    customSelect(element, onSelect);
    #target {
      user-select: none;
    }
    
    .text-selected {
      background-color: blue;
      color: white;
    }
    <div id="target">
    Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
    </div>

    我用一些 cmets 记录了代码,但是如果您有任何疑问,请随时提出。

    希望对你有帮助:)

    【讨论】:

      猜你喜欢
      • 2021-06-13
      • 1970-01-01
      • 1970-01-01
      • 2020-08-01
      • 1970-01-01
      • 2020-12-23
      • 1970-01-01
      • 2017-01-12
      • 1970-01-01
      相关资源
      最近更新 更多