jjzjj

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

coder 2024-07-26 原文

更新#2

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

  1. 选择 a 上方或下方的字词标签
  2. 您只选择 a 正上方或下方的一行标签
  3. 您在 a 上方/下方选择了不止一行标签
  4. 您在任意 a 下方选择了不止一行标签

以下情况不起作用:

  1. 您选择 a 以上的行/更多行标记,然后是 a 下面的行/更多行标签

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

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


更新:

我不需要将每一行都包装在 p 中标签,我可以改用内联元素,例如 a , span , 或 label标签,带有 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元素可以是 spanlabel ,或任何通常的内联定位元素,以欺骗 iOS 允许我选择字母而不是 block 元素。

是否有更改代码以保留新行元素的方法?

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>

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

最佳答案

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

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

关于javascript - 在特定点插入字符但保留标签?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15733349/

有关javascript - 在特定点插入字符但保留标签?的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. Ruby 解析字符串 - 2

    我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?

  3. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  4. ruby-on-rails - unicode 字符串的长度 - 2

    在我的Rails(2.3,Ruby1.8.7)应用程序中,我需要将字符串截断到一定长度。该字符串是unicode,在控制台中运行测试时,例如'א'.length,我意识到返回了双倍长度。我想要一个与编码无关的长度,以便对unicode字符串或latin1编码字符串进行相同的截断。我已经了解了Ruby的大部分unicode资料,但仍然有些一头雾水。应该如何解决这个问题? 最佳答案 Rails有一个返回多字节字符的mb_chars方法。试试unicode_string.mb_chars.slice(0,50)

  5. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  6. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  7. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  8. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  9. ruby - 如何使用文字标量样式在 YAML 中转储字符串? - 2

    我有一大串格式化数据(例如JSON),我想使用Psychinruby​​同时保留格式转储到YAML。基本上,我希望JSON使用literalstyle出现在YAML中:---json:|{"page":1,"results":["item","another"],"total_pages":0}但是,当我使用YAML.dump时,它不使用文字样式。我得到这样的东西:---json:!"{\n\"page\":1,\n\"results\":[\n\"item\",\"another\"\n],\n\"total_pages\":0\n}\n"我如何告诉Psych以想要的样式转储标量?解

  10. ruby - 在院子里用@param 标签警告 - 2

    我试图使用yard记录一些Ruby代码,尽管我所做的正是所描述的here或here#@param[Integer]thenumberoftrials(>=0)#@param[Float]successprobabilityineachtrialdefinitialize(n,p)#initialize...end虽然我仍然得到这个奇怪的错误@paramtaghasunknownparametername:the@paramtaghasunknownparametername:success然后生成的html看起来很奇怪。我称yard为:$yarddoc-mmarkdown我做错了什么?

随机推荐