如何扩展选定的文本以包括整个单词



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

如此"测试句子"。应该成为"测试句子"one_answers" est句子"也应成为"测试句子"

这是我当前的代码:

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代码还可以。

编辑本德:

好的,这几乎在那里。我有超过50k的句子,用户选择是可变的,所以我必须做类似的事情:

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

但是,如果用户确实选择"测试句子",这将不匹配。

有趣的挑战。

下面的代码将选择与类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.

我通过在每个单词中添加跨度来使其正常工作。这个想法是您将无法选择少于一个跨度/单词,这将选择整个跨度。此答案不取决于jQuery

function customSelect(target, onSelect) {
  var oldHTML = target.innerHTML;
  
  // this is the regex that wraps the words with spans:
  target.innerHTML = oldHTML.replace(/[dw']+[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>

我用一些评论记录了代码,但是,如果您有任何疑问,请问。

希望它有帮助:)

最新更新