在特定点插入字符但保留标签?

时间:2013-03-31 19:07:10

标签: javascript jquery

更新#2

好的,随后会有更多测试。当我使用虚假间隔时,看起来代码工作正常,但正则表达式最终失败。具体而言,以下方案有效:

  1. 选择a标记
  2. 上方或下方的字词
  3. 您只能在a标记
  4. 的正上方或下方选择一行
  5. 您在a标记
  6. 上方/下方选择了多行
  7. 您可以在任何a标记
  8. 下方选择多行

    以下情况不起作用:

    1. 您选择a标记上方的一行/多行,然后选择a代码
    2. 下方的一行/多行

      当“不起作用”时会发生什么,它会从DOM中删除a标记spacer。这可能是正则表达式的问题......

      基本,当您选择a标记周围的文字时,它会失败。


      更新

      我不需要在p标记中包含每一行,而是可以使用内联元素,例如aspanlabel标记,display:inline-block和高度+宽度作为新的线元素(<br />)。这应该可以更容易地修改代码,因为唯一需要更改的部分是我在文本之间获取文本。我只需要更改该部分selectedText.textContent,以检索也在边界内的HTML而不仅仅是文本。


      我正在创建一个要求用户选择文字的Phonegap。但是,我需要对文本选择进行精细控制,并且无法再将整个文本内容放在pre样式的p标记中。相反,我需要使用类似<a class="space"></a>的代码表示换行符,以便可以精确突出显示正确的单词。当我的文字看起来像这样:

      <p class="text">This is line one
      
      Line two
      
      Line three
      </p>
      

      并且有.text{ white-space:pre-wrap },以下代码允许我选择单词,然后用span元素包装文本以显示文本突出显示:

      $("p").on("copy", highlight);
      
      function highlight() {
          var text = window.getSelection().toString();
          var selection = window.getSelection().getRangeAt(0);
          var selectedText = selection.extractContents();
          var span = $("<span class='highlight'>" + selectedText.textContent + "</span>");
          selection.insertNode(span[0]);
          if (selectedText.childNodes[1] != undefined) {
              $(selectedText.childNodes[1]).remove();
          }
          var txt = $('.text').html();
          $('.text').html(txt.replace(/<\/span>(?:\s)*<span class="highlight">/g, ''));
          $(".output ul").html("");
          $(".text .highlight").each(function () {
              $(".output ul").append("<li>" + $(this).text() + "</li>");
          });
          clearSelection();
      }
      
      function clearSelection() {
          if (document.selection) {
              document.selection.empty();
          } else if (window.getSelection) {
              window.getSelection().removeAllRanges();
          }
      }
      

      此代码可以很好地工作,但不是每行都用spacer标记分隔。新文本如下:

      <p class="text">
          Line one
          <a class="space"></a>
          Line two
          <a class="space"></a>
          Line three
      </p>
      

      当我修改上面的代码以使用由<a class="space"></a>表示的新行时,代码失败。它只检索选择的文本,而不是HTML(selectedText.textContent)。我不确定正则表达式是否也会因a元素作为新行而失败。 a元素可以是spanlabel,也可以是任何正常的内联定位元素,以诱骗iOS允许我选择字母而不是块元素。

      是否有更改代码以保留新的线元素?

      jsFiddle:http://jsfiddle.net/charlescarver/39TZ9/3/

      所需的输出应如下所示:

      如果突出显示“第一行”文字:

      <p class="text">
          <span class="highlight">Line one</span>
          <a class="space"></a>
          Line two
          <a class="space"></a>
          Line three
      </p>
      

      如果突出显示“第一行第二行”文本:

      <p class="text">
          <span class="highlight">Line one
          <a class="space"></a>
          Line two</span>
          <a class="space"></a>
          Line three
      </p>
      

      当然,不同的部分和单个字母也可以突出显示,而不是全行。

2 个答案:

答案 0 :(得分:0)

这是一个支持您要求的所有功能的解决方案:

<强> HTML

<p class="text">
    First Line
    <a class="space"></a>
    <a class="space"></a>
    Second Line
    <span class="space"></span>
    Third Line
    <label class="space"></label>
    Forth Line
</p>
<ul class="output"></ul>

<强> CSS

.space {
    display: inline-block;
    width: 100%;
}
.highlighting {
    background-color: green;
}

<强>的JavaScript

var text,
    output,
    unwrapContents,
    mergeElements,
    clearSelection,
    clearHighlighting,
    mergeHighlighting,
    handleCopy;

unwrapContents = function unwrapContents(element) {
    while(element.firstChild !== null) {
        element.parentNode.insertBefore(element.firstChild, element);
    }
    element.parentNode.removeChild(element);
};

mergeElements = function mergeElements(startElement, endElement) {
    var currentElement;
    endElement = endElement.nextSibling;
    while((currentElement = startElement.nextSibling) !== endElement) {
        startElement.appendChild(currentElement);
    }
};

clearSelection = function clearSelection() {
    if (document.selection) {
        document.selection.empty();
    } else if (window.getSelection) {
        window.getSelection().removeAllRanges();
    }
};

clearHighlighting = function clearHighlighting(target, exception) {
    $('.highlighting', target).each(function(index, highlighting) {
        if(highlighting !== exception) {
            unwrapContents(highlighting);
        }
    });
    target.normalize();
};

mergeHighlighting = function mergeHighlighting() {
    var i, j;
    // Remove internal highlights
    $('.highlighting', text).filter(function() {
        return this.parentNode.className === 'highlighting';
    }).each(function(index, highlighting) {
        unwrapContents(highlighting);
    });
    text.normalize();
    // Merge adjacent highlights
    first:
    for(i=0; i<text.childNodes.length-1; i++) {
        if(text.childNodes[i].className === 'highlighting') {
            for(j=i+1; j<text.childNodes.length; j++) {
                if(text.childNodes[j].className === 'highlighting') {
                    mergeElements(text.childNodes[i], text.childNodes[j--]);
                    unwrapContents(text.childNodes[i].lastChild);
                } else {
                    switch(text.childNodes[j].nodeType) {
                        case 1:
                            if(text.childNodes[j].className !== 'space') {
                                continue first;
                            }
                            break;
                        case 3:
                            if(text.childNodes[j].textContent.trim() !== '') {
                                continue first;
                            }
                            break;
                    }
                }
            }
        }
    }
};

handleCopy = function handleCopy() {
    var range,
        highlighting,
        item;

    // Highlighting
    range = window.getSelection().getRangeAt(0);
    highlighting = document.createElement('span');
    highlighting.className = 'highlighting';
    highlighting.appendChild(range.cloneContents());
    range.deleteContents();
    range.insertNode(highlighting);

    // Output
    item = document.createElement('li');
    item.innerHTML = highlighting.innerHTML;
    clearHighlighting(item);
    output.appendChild(item);

    // Cleanup
    mergeHighlighting();
    clearSelection();
};

$(function(){
    text = $('.text')[0];
    output = $('.output')[0];
    $(text).on('copy', handleCopy);
});

以下是一个工作示例http://jsbin.com/efohit/3/edit

答案 1 :(得分:-1)

好吧,我想出了一个解决方案,也很简单。

如果.text看起来像这样:

<p class="text">Line one
<a class="space"></a>Line two
<a class="space"></a>Line three</p>

使用上面的确切标记和换行符,然后我可以找到每个\n并用间隔元素替换它。

if (textStr.indexOf("\n") >= 0) {
    textStr = textStr.replace(/\n/g, "\n<a class='space'></a>");
}

这根本不是多功能的,如果有多个换行符,如果标签不同等,则会失败。所以,我鼓励任何有更好方法的人回答这个问题!它没那么难,我想通了。