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

时间:2015-05-04 13:03:02

标签: javascript jquery

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

所以“测试句子。”应该成为“测试句” 而“est sentenc”也应该成为“测试句”

这是我目前的代码:

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

但是,如果用户选择“测试句子”,那么这将不会匹配。

2 个答案:

答案 0 :(得分:2)

有趣的挑战。

下面的代码将选择内容包含在类selected的范围内。

它抓取新元素的nodeValuepreviousSibling的{​​{1}} - 通过拆分非单词字符获取相应的文本,然后弹出(nextSibling)或转移(previousSibling)。

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

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



selected

$(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;
}




答案 1 :(得分:0)

我设法通过为每个单词添加跨度来实现它。这个想法是你将无法选择少于一个跨度/单词,这样就可以选择整个跨度。 这个答案不依赖于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>

我在代码中记录了一些评论,但如果您有任何疑问,请随时提出。

希望有所帮助:)