7

更新#2

好的,接下来会进行更多测试。当我使用人造垫片时,看起来代码工作正常,但正则表达式最终失败。具体来说,以下场景有效:

  1. a选择标签上方或下方的单词
  2. a您只选择标签正上方或正下方的一行
  3. a您在标签上方/下方选择了多行
  4. a您在任何标签下方选择了多行

以下场景不起作用:

  1. 您选择标签上方的行/多行,然后选择标签a下方的行/多行a

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

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


更新:

我不需要将每一行包装在一个p标签中,我可以使用一个内联元素,例如一个a、、spanlabel标签,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();
    }
}

此代码运行良好,但在每行由间隔标记分隔时就不行了。新文本如下所示:

<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元素可以是spanorlabel或任何通常内联定位的元素,以欺骗 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>

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

4

2 回答 2

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

于 2013-04-08T22:19:06.523 回答
-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>");
}

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

于 2013-04-03T05:57:48.687 回答