【问题标题】:Insert character at specific point but preserving tags?在特定点插入字符但保留标签?
【发布时间】:2013-04-09 20:32:41
【问题描述】:

更新 #2

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

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

以下情况不起作用:

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

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

基本上,当您选择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();
    }
}

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

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

当我修改上述代码以使用由&lt;a class="space"&gt;&lt;/a&gt; 表示的新行时,代码将失败。它只检索选择的文本,而不是 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>

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

【问题讨论】:

  • 立即弹出的一件事是,不同类型的引号不会触发正则表达式中的匹配。第一个 highlight 使用单引号,第二个使用双引号。不过可能无法解决您的问题。
  • 这不是问题,因为它是作为双引号插入的。我只使用单引号,所以我不必在双引号内转义双引号。
  • 我更新了一个例子
  • 每次点击copy 时都会删除书挡,因此只有一组。在其他 highlight 类中整理书挡会有点棘手,但我认为用 jQuery 创建逻辑并不难。

标签: javascript jquery


【解决方案1】:

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

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

【讨论】:

  • 不正确。这不包括在重叠时合并高光的选项,并且一次只在屏幕上显示一个高光。
  • @Charlie 我更新了实现。现在相邻的、重叠的和嵌套的高光被合并了。
  • 这与我的代码相比如何,由于某种原因,行投票?
【解决方案2】:

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

如果.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>");
}

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-13
    • 1970-01-01
    • 1970-01-01
    • 2019-07-31
    • 2021-04-17
    • 2011-05-24
    • 2020-12-01
    • 2016-03-17
    相关资源
    最近更新 更多