浏览器内文本的交互式选择突出显示

Posted

技术标签:

【中文标题】浏览器内文本的交互式选择突出显示【英文标题】:Interactive Selection highlighting of text inside the browser 【发布时间】:2015-05-03 10:33:38 【问题描述】:

我需要的是通过 javascript 在浏览器中对文本进行交互式选择突出显示。

更具体地说,假设我在 div 元素中有一堆文本,如下所示:

<div>The quick brown fox jumps over the lazy dog ... </div>

现在我要做的是选择该文本的某个范围并突出显示它(即“棕色狐狸”)。我已经通过使用rangy.js 完成了这项工作。

现在我真的想让这个选择/突出显示更具交互性,以便用户可以抓住选择的开头或结尾(使用某种手柄)并通过拖动开头或结尾交互式地调整选择的大小当前选择到新的起点或终点。

我试图用谷歌搜索,但找不到任何东西。更糟糕的是,我完全不知道我应该如何开始实施这样的事情。

也许有人可以为我指出正确的方向,或者更好的是已经对如何开始实施这样的事情有了一些想法。

非常感谢您的帮助...

【问题讨论】:

听起来您想在桌面浏览器中重现移动样式突出显示。我说的对吗? 好吧,我不知道您所说的“移动风格突出显示”是什么意思,但如果它的行为与上述完全一样,那么可以。 例如,当您在 android 中突出显示一串文本时,您将在选择的任一侧获得句柄,您可以拖动该句柄来修改现有选择。它可能会为您提供一个提示,作为寻找答案的方向,因为开发人员已经在其他平台上明确解决了这个问题,所以您可能想从那里开始。是否有它的 JS 库是另一回事。这是一个谷歌图片搜索google.com/… 【参考方案1】:

这个问题相当复杂,并且由于跨浏览器缺乏一致性而受到阻碍。但是,我在一定程度上成功解决了这个问题。

Test out my solution with this jsfiddle

我不保证这在所有情况下都有效,到目前为止我的测试仅限于文本。还有一些问题需要解决,我将在不久的将来解决。不过,我认为它已经准备好在这里发布了。

方法

问题可以分解为几个步骤。首先,我们需要在用户的选择周围插入标记。这并不太难,尽管必须解决几个设计决策。经过一些实验后,我决定将 span 直接插入到文档中的标记位置;然后更容易在这些标记之间选择内容。下面是插入标记方法。

function insertMarker (isBefore) 
    var range;
    
    if (window.getSelection) 
        // IE9+ and non-IE
        var sel = window.getSelection();
        
        if (sel.getRangeAt) 
            range = window.getSelection().getRangeAt(0);
            range.collapse(isBefore);
        
     else if (document.selection && document.selection.createRange) 
        // IE < 9
        range = document.selection.createRange();
        range.collapse(isBefore);
    
    
    // Create the marker element and insert it into the DOM.
    if (range) 
        range.insertNode(createMarker(isBefore));
    

其次,用户必须能够在文档周围拖动这些标记,以便它们在字符/可选内容之间锁定。这可以使用document.caretPositionFromPoint(x, y)(标准)或document.caretRangeFromPoint(x, y)(WebKit)来实现。

第三,当标记移动时,选择必须更新以反映这种变化。这可以使用 range.setStartAfterrange.setEndBefore 来实现,尽管 Range 实现不同,但大多数浏览器都支持它们。

function selectSelection () 
    if (window.getSelection) 
        // IE9+ and non-IE
        var sel = window.getSelection();
        var range = document.createRange();
        range.setStartAfter($(".marker").get(0));
        range.setEndBefore($(".marker").get(1));
        sel.removeAllRanges();
        sel.addRange(range);
    
    else if (document.selection && document.selection.createRange) 
        // IE < 9
        var range = document.selection.createRange();
        range.setStartAfter($(".marker").get(0));
        range.setEndBefore($(".marker").get(1));
    

【讨论】:

【参考方案2】:

试试

html

<div id="selectable"></div>
<br />
<hr />
<button id="clear">clear selections</button>
<hr />
<br />
<div id="selections">
  <div class="selected"></div>
</div>

css

#selectable .ui-selecting 
  background: #FECA40;

#selectable .ui-selected 
  background: #F39814;
  color: white;

#selections 
  width: 100%;
  height: 400px;

#clear 
  position: relative;
  left: calc(37%);

.selected 
  width: 150px;
  height: 150px;
  overflow: hidden;

.selected,
.text 
  display: block;
  background-color: rgb(225, 225, 225);

.ui-widget-content 
  border: 1px dotted rgba(170, 170, 170, .25);

.ui-widget-conent span 
  display: inline-block;
  padding: 4px;
  margin: 4px;

js

  var selectable = $("#selectable") // selectable words
  , selections = $("#selections") // selected words container parent
  , selected = $(".selected") // selected words container
  , clear = $("#clear") // clear all
  , sel = ".ui-selected" // selected word
  , content = ".ui-widget-content" // selectable words element `class`
  , text = ".text" // selected words draggable, resizable container `class`
  , handles =  // resize handles
  , str = "The quick brown fox jumps over the lazy dog ..." // text, `string`
    // split `str`, return array of words
  , words = str.split(" ").map(function(word, i) 
      $("<span />", 
        "class": content.slice(1),
        "text": word,
        "css": 
          "margin": "2px"
        
      ).appendTo(selectable)
    );
  // append `handles` to `text` container
  $.each(["nw", "ne", "sw", "se"], function(_, handle) 
    var elem = $("<div />", 
        "class": "ui-resizable-handle ui-resizable-" + handle,
        "css": 
          "width": "calc(12px + 5%)",
          "height": "calc(12px + 5%)",
          "border-radius": "50%",
          "background": "#000"
        
      )
      .appendTo(selected)
      .parent().find(":last")
      .css(
        "right": "-5px",
        "bottom": "-5px"
      );
    handles[handle] = elem[0];
  );
  // `selected` settings
  // set `selected` `display` to `none`
  selected
  .hide(0)
  .resizable(
      handles: handles
  )
  .draggable(
      containment: "parent"
  );
  // collect selected words at `selectable.data("selections", [])` array,
  // append words to `text`,
  // append `text` to `selected`,
  // set `selected` `display` to `block`
  selectable.data("selections", [])
    .selectable(
      selected: function(event, ui) 
        $(this).data("selections").push($(ui.selected).text())
      ,
      stop: function() 
        selected
          .find(".text")
          .remove()
          .addBack()
          .prepend(
            $("<div />", 
              "class": "text",
              "text": $(this).data("selections").join(" "),
              "css": 
                "position": "relative",
                "display": "block",
                "padding": "calc(15%)",
                "height": "calc(50%)",
                "white-space": "pre-line",
                "overflow": "hidden"
              
            )
          ).show(0)
      
    );
  // remove selected words from `selectable.data("selections", [])` array,
  // remove `sel` `class` from `selectable` words,
  // remove `text`,
  // set `selected` display to `none`
  clear.on("click", function(e) 
    selectable.data("selections", [])
      .find(sel).removeClass(sel.slice(1));
    selections.find(text).remove()
      .addBack().find(selected).hide(0)
  );

jsfiddle http://jsfiddle.net/guest271314/dpt9bn0n/

请参阅 jQuery UI Selectable、Draggable、Resizable

$(function() 

  var selectable = $("#selectable") // selectable words
  , selections = $("#selections") // selected words container parent
  , selected = $(".selected") // selected words container
  , clear = $("#clear") // clear all
  , sel = ".ui-selected" // selected word
  , content = ".ui-widget-content" // selectable words element `class`
  , text = ".text" // selected words draggable, resizable container `class`
  , handles =  // resize handles
  , str = "The quick brown fox jumps over the lazy dog ..." // text, `string`
    // split `str`, return array of words
  , words = str.split(" ").map(function(word, i) 
      $("<span />", 
        "class": content.slice(1),
        "text": word,
        "css": 
          "margin": "2px"
        
      ).appendTo(selectable)
    );
  // append `handles` to `text` container
  $.each(["nw", "ne", "sw", "se"], function(_, handle) 
    var elem = $("<div />", 
        "class": "ui-resizable-handle ui-resizable-" + handle,
        "css": 
          "width": "calc(12px + 5%)",
          "height": "calc(12px + 5%)",
          "border-radius": "50%",
          "background": "#000"
        
      )
      .appendTo(selected)
      .parent().find(":last") // `se` 
      .css(
        "right": "-5px",
        "bottom": "-5px"
      );
    handles[handle] = elem[0];
  );
  // `selected` settings
  // set `selected` `display` to `none`
  selected
    .hide(0)
    .resizable(
      handles: handles
    )
    .draggable(
      containment: "parent"
    );
  // collect selected words at `selectable.data("selections", [])` array,
  // append words to `text`,
  // append `text` to `selected`,
  // set `selected` `display` to `block`
  selectable.data("selections", [])
    .selectable(
      selected: function(event, ui) 
        $(this).data("selections").push($(ui.selected).text())
      ,
      stop: function() 
        selected
          .find(".text")
          .remove()
          .addBack()
          .prepend(
            $("<div />", 
              "class": "text",
              "text": $(this).data("selections").join(" "),
              "css": 
                "position": "relative",
                "display": "block",
                "padding": "calc(15%)",
                "height": "calc(50%)",
                "white-space": "pre-line",
                "overflow": "hidden"
              
            )
          ).show(0)
      
    );
  // remove selected words from `selectable.data("selections", [])` array,
  // remove `sel` `class` from `selectable` words,
  // remove `text`,
  // set `selected` display to `none`
  clear.on("click", function(e) 
    selectable.data("selections", [])
      .find(sel).removeClass(sel.slice(1));
    selections.find(text).remove()
      .addBack().find(selected).hide(0)
  );

  // do stuff
  var phrase = [0, 1, 2, 8, 4, 5, 6, 7, 3, 9];
  setTimeout(function() 
    $.when(
        selectable.queue("phrase", $.map(phrase, function(word, i) 
          return function(next) 
            return $.when(!$(this).find(content).eq(word)
              .addClass("ui-selecting").parent()
              .data("ui-selectable")._mouseStop(false) && $(this)
            ).then(function(el) 
              return el.delay(2000);
            ).then(next)
          
        )
        )
        .dequeue("phrase").promise("phrase")
      , clear
    )
    .then(function(el, button) 
        button.trigger("click")
    );
  , 2713);

);
#selectable .ui-selecting 
  background: #FECA40;

#selectable .ui-selected 
  background: #F39814;
  color: white;

#selections 
  width: 100%;
  height: 400px;

#clear 
  position: relative;
  left: calc(37%);

.selected 
  width: 150px;
  height: 150px;
  overflow: hidden;

.selected,
.text 
  display: block;
  background-color: rgb(225, 225, 225);

.ui-widget-content 
  border: 1px dotted rgba(170, 170, 170, .25);

.ui-widget-conent span 
  display: inline-block;
  padding: 4px;
  margin: 4px;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<script src="http://code.jquery.com/ui/1.11.3/jquery-ui.js"></script>
<link href="http://code.jquery.com/ui/1.11.3/themes/smoothness/jquery-ui.css" 
      rel="stylesheet" />
<div id="selectable"></div>
<br />
<hr />
<button id="clear">clear selections</button>
<hr />
<br />
<div id="selections">
  <div class="selected"></div>
</div>

【讨论】:

您好,首先非常感谢您的意见。可悲的是,这并不是我真正想要的。我正在寻找一种方法来调整选择本身的大小,而不是显示所选元素的单独文本区域。上面 robabby 的最后一条评论几乎总结了我正在寻找的内容。这是link 的外观。 @evermean 调整大小选择 element font-size 或两者兼而有之? 我不关心字体大小,我需要选择文本的某个部分,然后能够调整该选择的大小(...完全如上面链接的图片所示。 )。 通过调整大小,仅调整element content-box 的大小?即,与上面的可拖动元素相同或相似,尽管在选择点添加可调整大小的“句柄”到选择? 它应该是这样工作的。 Video。相关部分从 0:30 开始......希望这有助于澄清它。

以上是关于浏览器内文本的交互式选择突出显示的主要内容,如果未能解决你的问题,请参考以下文章

CSS 或 Javascript 禁用从 CTRL + A 突出显示的文本选择

使用 Javascript/JQuery 为移动网络(Android、iOS、Windows Phone)突出显示/选择元素上的文本

更改 QGraphicsTextItem 内文本的突出显示颜色

突出显示 textarea 中的所有文本

选择和突出显示标签上的文本

文本选择突出显示太多