草庐IT

javascript - 继续将文本动态地放入单独的<p>段落中吗?

coder 2023-07-05 原文

以下fiddle允许将文本粘贴到<textarea>中,并动态生成为包含相同数量字符的<p>相等的段落。

发生的问题是;先前动态生成的段落中的文本<p>在每个标记中溢出,并且无法继续正确地进入下一个动态段落。因此,用户是否可以按Enter键并将该内容向下移动到下一个现有段落中,同时仍然可以动态,自动地保持现有格式?

如果可以提供一个新的Fiddle,那将不胜感激,因为我还是编码新手。再次, fiddle 可以找到here

更新:是否可以在生成段落之后,让用户按Enter键,如果可能,将其内容无缝地向下移动到下面的段落中?按下退格按钮时内容是否也要应用到上面的内容中?发生的问题是,由于按css中的overflow属性,当按Enter键时,文本似乎隐藏了文本。



$(function() {
    $("#Go").on('click', function() {
        var theText = $('textarea').val();
        var numberOfCharacters = 300;
        while (theText.length) {
            while (theText.length > numberOfCharacters &&
                theText.charAt(numberOfCharacters) !== ' ') {
                numberOfCharacters++;
            }
            $("#text_land").append("<br><\/br><p>" + theText.substring(
                    0, numberOfCharacters) +
                "<\/p><br><\/br>");
            theText = theText.substring(numberOfCharacters);
            numberOfCharacters = 300;
            $('p').attr('contenteditable', 'true');
            $("p").addClass("text");
        }
    })
})
$('select').on('change', function() {
    var targets = $('#text_land p'),
        property = this.dataset.property;
    targets.css(property, this.value);
}).prop('selectedIndex', 0);
(end);
@media print {
    p {
        page-break-inside: avoid;
    }
}

p {
    position: relative;
}

@media print {
    .no-print,.no-print * {
        display: none !important;
    }
}

p {
    border-style: solid;
    color: #000;
    display: block;
    text-align: justify;
    border-width: 5px;
    font-size: 19px;
    overflow: hidden;
    height: 300px;
    width: 460px;
    word-wrap: break-word;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


 <div align="center">
        <h4 align="center"><u>Paste text in the field below to divide text into
        paragraphs.</u></h4><br>
        <br>
        <textarea placeholder="Type text here, then press the button below." cols="50" id="textarea1" rows="10">
</textarea><br>
        <br>
        <button id="Go">Divide Text into Paragraphs!</button>
    </div>
    <hr>
    <h2 align="center">Divided Text Will Appear Below:</h2>
    <div>
        <div align="center" id="text_land" style="font-family: monospace">
        </div>
    </div>

最佳答案

possible for the user to press enter and move that content down into the next existing paragraph, while still keeping the existing formatting dynamically and automatically



如果我理解正确,那么您想要的是,一旦将文本划分为多个段落,然后用户将一些文本添加到其中一个文本中,然后按Enter键,那么其余文本应流入下一个段落中,以与早点做。

同样,当用户在段落的开头按下BackSpace时,文本将再次返回到上一个段落,而溢出的文本将像之前一样平均分配到其他段落中。

作为一种算法,您需要的是这样的:
  • 将初始文本分成相等的块,并按需要动态分配到创建这些p的段落中。
  • 侦听那些keyup元素上的p事件
  • 如果按Enter键,
  • 3.1从按下Enter的位置提取剩余文本
  • 3.2从接下来的所有段落中提取文本,并以在
  • 上方提取的溢出文本为前缀
  • 3.3删除下一个所有段落并分发组合文本,就像我们在步骤1中所做的一样。
  • 如果按下的键是BackSpace,
  • 4.1检查它是否在段落的开头以及是否存在前一段
  • 4.2提取该段落的文本,然后将其添加到下一个所有段落的文本中
  • 4.3删除下一个所有段落,包括当前段落,并将提取的文本附加到上一个段落。
  • 4.4分发组合文本,就像我们在步骤1中所做的一样。

  • 使用该粗略算法,您可以开始编码,如下所示:

    注1 :这都是JavaScript,没有jQuery。
    注2 :这过于简化,您将需要进一步优化和解决所有极端情况。

    缓存必需的元素并绑定(bind)事件处理程序:
    var btn = document.getElementById('go'), 
        textarea = document.getElementById('textarea1'), 
        content = document.getElementById('content');
    
    btn.addEventListener('click', initialDistribute);
    content.addEventListener('keyup', handleKey);
    

    分发textarea中的初始文本,删除现有段落(如果有):
    function initialDistribute() {
        var text = textarea.value;
        while (content.hasChildNodes()) { content.removeChild(content.lastChild); }
        rearrange(text);
    }
    

    通过动态创建所需数量的段落来重新排列/分发文本的逻辑:
    function rearrange(text) {
        var chunks = text.match(/.{1,100}/g) || [];
        chunks.forEach(function(str, idx) {
            para = document.createElement('P');
            para.setAttribute('contenteditable', true);
            para.textContent = str;
            content.appendChild(para);
        });
    }
    

    注3 :在此示例中,我使用了100个字符来分割文本。同样,这不会占用空格,并且会在其中分隔单词。您将需要在代码中执行此操作。 (#参见下面的编辑)

    用于捕获Enter键(键码13)和BackSpace键(键码8)的事件处理程序。另外,看看该元素是否是p元素:
    function handleKey(e) {
        var para = e.target, position, 
            key, fragment, overflow, remainingText;
        key = e.which || e.keyCode || 0;
        if (para.tagName != 'P') { return; }
        if (key != 13 && key != 8) { return; }
        ...
    

    获取光标位置,以确定是否在段落开头处按了BackSpace:
    position = window.getSelection().getRangeAt(0).startOffset;    
    

    如果按下Enter键,则在当前段落的最后一个子句之后提取文本(按下Enter键时contenteditable会生成一个div),删除该节点,在此之后的所有段落之前保留其余文本,然后删除其余段落。
    if (key == 13) {
        fragment = para.lastChild; overflow = fragment.textContent;
        fragment.parentNode.removeChild(fragment); 
        remainingText = overflow + removeSiblings(para, false);
        rearrange(remainingText);
    }
    

    如果按了BackSpace,请检查是否有前一段,并且光标在开头。如果是,请从所有后续段落(包括当前段落)中提取剩余文本,同时将其删除:
    if (key == 8 && para.previousElementSibling && position == 0) {
        fragment = para.previousElementSibling;
        remainingText = removeSiblings(fragment, true);
        rearrange(remainingText);
    }
    

    从后续段落中提取文本并将其删除的逻辑:
    function removeSiblings(elem, includeCurrent) {
        var text = '', next;
        if (includeCurrent && !elem.previousElementSibling) { 
            parent = elem.parentNode; text = parent.textContent;
            while (parent.hasChildNodes()) { parent.removeChild(parent.lastChild); }
        } else {
            elem = includeCurrent ? elem.previousElementSibling : elem;
            while (next = elem.nextSibling) { 
                text += next.textContent; elem.parentNode.removeChild(next);
            }
        }
        return text;
    }
    

    综上所述,这是一个有效的代码段:

    代码段:

    var btn = document.getElementById('go'), 
    	textarea = document.getElementById('textarea1'), 
    	content = document.getElementById('content'), 
        chunkSize = 100;
        
    btn.addEventListener('click', initialDistribute);
    content.addEventListener('keyup', handleKey);
    
    function initialDistribute() {
        var text = textarea.value;
        while (content.hasChildNodes()) {
            content.removeChild(content.lastChild);
        }
        rearrange(text);
    }
    
    function rearrange(text) {
        var	chunks = splitText(text, false);
        chunks.forEach(function(str, idx) {
            para = document.createElement('P');
            para.setAttribute('contenteditable', true);
            para.textContent = str;
            content.appendChild(para);
        });
    }
    
    function handleKey(e) {
        var para = e.target, position, 
            key, fragment, overflow, remainingText;
        key = e.which || e.keyCode || 0;
        if (para.tagName != 'P') { return; }
        if (key != 13 && key != 8) { return; }
    		position = window.getSelection().getRangeAt(0).startOffset;    
        if (key == 13) {
            fragment = para.lastChild;
            overflow = fragment.textContent;
            fragment.parentNode.removeChild(fragment); 
            remainingText = overflow + removeSiblings(para, false);
            rearrange(remainingText);
        }
        if (key == 8 && para.previousElementSibling && position == 0) {
            fragment = para.previousElementSibling;
            remainingText = removeSiblings(fragment, true);
            rearrange(remainingText);
        }
    }
    
    function removeSiblings(elem, includeCurrent) {
        var text = '', next;
        if (includeCurrent && !elem.previousElementSibling) { 
            parent = elem.parentNode; 
    		text = parent.textContent;
            while (parent.hasChildNodes()) {
                parent.removeChild(parent.lastChild);
            }
        } else {
            elem = includeCurrent ? elem.previousElementSibling : elem;
            while (next = elem.nextSibling) { 
                text += next.textContent;
                elem.parentNode.removeChild(next);
            }
        }
        return text;
    }
    
    function splitText(text, useRegex) {
    	var chunks = [], i, textSize, boundary = 0;
        if (useRegex) { 
            var regex = new RegExp('.{1,' + chunkSize + '}\\b', 'g');
            chunks = text.match(regex) || [];
        } else {
    		for (i = 0, textSize = text.length; i < textSize; i = boundary) {
    			boundary = i + chunkSize;
    			if (boundary <= textSize && text.charAt(boundary) == ' ') {
    				chunks.push(text.substring(i, boundary));
    			} else {
    				while (boundary <= textSize && text.charAt(boundary) != ' ') { boundary++; }
    				chunks.push(text.substring(i, boundary));
    			}
    		}
    	}
    	return chunks;
    }
    * { box-sizing: border-box; padding: 0; margin: 0; }
    body { font-family: monospace; font-size: 1em; }
    h3 { margin: 1.2em 0; }
    div { margin: 1.2em; }
    textarea { width: 100%; }
    button { padding: 0.5em; }
    p { padding: 1.2em 0.5em; margin: 1.4em 0; border: 1px dashed #aaa; }
    <div>
      <h3>Paste text in the field below to divide text into
            paragraphs..</h3>
      <textarea placeholder="Type text here, then press the button below." id="textarea1" rows="5" ></textarea><br/><br/>
      <button id="go">Divide Text into Paragraphs</button>
    </div>
    <hr>
    <div>
      <h3>Divided Text Will Appear Below:</h3>
      <div id="content"></div>
    </div>


    还有一个 fiddle 供您使用:

    fiddle :https://jsfiddle.net/abhitalks/jwnnn5oy/

    编辑1:

    修复了用于打破单词边界的正则表达式。还添加了相同的非正则表达式过程代码(与Op的原始代码相似),以演示Op如何将其其他代码段纳入并整合到其中。

    注4 :关于Op对使用jQuery的评论,它与手头的问题无关。 jQuery只不过是JavaScript而已,对于他们来说,将片段合并到更大的代码库中应该是微不足道的。

    变更集:添加了功能splitText

    编辑2:

    根据您的评论,如果您希望重新分配在用户键入时自动发生...那么您将需要计算该段落中文本的长度,并查看是否超过了块大小。如果是这样,则从该段开始重新分配。向后退格。

    我最初发布了解决方案,以满足您的要求,即当用户按下任意文本以在其中打断并将其分发给后续段落时,您需要这样做。我不建议在用户输入时自动执行此操作,因为更改对于用户而言将太麻烦。

    代码段2:

    var btn = document.getElementById('go'), 
    	textarea = document.getElementById('textarea1'), 
    	content = document.getElementById('content'), 
        chunkSize = 100;
        
    btn.addEventListener('click', initialDistribute);
    content.addEventListener('keyup', handleKey);
    
    function initialDistribute() {
        var text = textarea.value;
        while (content.hasChildNodes()) {
            content.removeChild(content.lastChild);
        }
        rearrange(text);
    }
    
    function rearrange(text) {
        var	chunks = splitText(text, false);
        chunks.forEach(function(str, idx) {
            para = document.createElement('P');
            para.setAttribute('contenteditable', true);
            para.textContent = str;
            content.appendChild(para);
        });
    }
    
    function handleKey(e) {
        var para = e.target, position, 
            key, fragment, overflow, remainingText;
        key = e.which || e.keyCode || 0;
        if (para.tagName != 'P') { return; }
        if (key != 13 && key != 8) { redistributeAuto(para); return; }
    		position = window.getSelection().getRangeAt(0).startOffset;    
        if (key == 13) {
            fragment = para.lastChild;
            overflow = fragment.textContent;
            fragment.parentNode.removeChild(fragment); 
            remainingText = overflow + removeSiblings(para, false);
            rearrange(remainingText);
        }
        if (key == 8 && para.previousElementSibling && position == 0) {
            fragment = para.previousElementSibling;
            remainingText = removeSiblings(fragment, true);
            rearrange(remainingText);
        }
    }
    
    function redistributeAuto(para) {
    	var text = para.textContent, fullText;
    	if (text.length > chunkSize) {
    		fullText = removeSiblings(para, true);
    	}
    	rearrange(fullText);
    }
    
    function removeSiblings(elem, includeCurrent) {
        var text = '', next;
        if (includeCurrent && !elem.previousElementSibling) { 
            parent = elem.parentNode; 
    		text = parent.textContent;
            while (parent.hasChildNodes()) {
                parent.removeChild(parent.lastChild);
            }
        } else {
            elem = includeCurrent ? elem.previousElementSibling : elem;
            while (next = elem.nextSibling) { 
                text += next.textContent;
                elem.parentNode.removeChild(next);
            }
        }
        return text;
    }
    
    function splitText(text, useRegex) {
    	var chunks = [], i, textSize, boundary = 0;
        if (useRegex) { 
            var regex = new RegExp('.{1,' + chunkSize + '}\\b', 'g');
            chunks = text.match(regex) || [];
        } else {
    		for (i = 0, textSize = text.length; i < textSize; i = boundary) {
    			boundary = i + chunkSize;
    			if (boundary <= textSize && text.charAt(boundary) == ' ') {
    				chunks.push(text.substring(i, boundary));
    			} else {
    				while (boundary <= textSize && text.charAt(boundary) != ' ') { boundary++; }
    				chunks.push(text.substring(i, boundary));
    			}
    		}
    	}
    	return chunks;
    }
    * { box-sizing: border-box; padding: 0; margin: 0; }
    body { font-family: monospace; font-size: 1em; }
    h3 { margin: 1.2em 0; }
    div { margin: 1.2em; }
    textarea { width: 100%; }
    button { padding: 0.5em; }
    p { padding: 1.2em 0.5em; margin: 1.4em 0; border: 1px dashed #aaa; }
    <div>
      <h3>Paste text in the field below to divide text into
            paragraphs..</h3>
      <textarea placeholder="Type text here, then press the button below." id="textarea1" rows="5" ></textarea><br/><br/>
      <button id="go">Divide Text into Paragraphs</button>
    </div>
    <hr>
    <div>
      <h3>Divided Text Will Appear Below:</h3>
      <div id="content"></div>
    </div>


    fiddle 2:https://jsfiddle.net/abhitalks/hvhr4ds8/

    注5 :在 fiddle 中,首先按回车键可在它们之间打断一些文本,以便您可以看到键入时重新分配的过程。另外,请注意,由于不打断一个单词的逻辑,在重新分发之前,还需要几个字符。

    变更集:添加了功能redistributeAuto

    关于javascript - 继续将文本动态地放入单独的<p>段落中吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34334156/

    有关javascript - 继续将文本动态地放入单独的<p>段落中吗?的更多相关文章

    1. ruby - 我需要将 Bundler 本身添加到 Gemfile 中吗? - 2

      当我使用Bundler时,是否需要在我的Gemfile中将其列为依赖项?毕竟,我的代码中有些地方需要它。例如,当我进行Bundler设置时:require"bundler/setup" 最佳答案 没有。您可以尝试,但首先您必须用鞋带将自己抬离地面。 关于ruby-我需要将Bundler本身添加到Gemfile中吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/4758609/

    2. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

      我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

    3. ruby-on-rails - rspec should have_select ('cars' , :options => ['volvo' , 'saab' ] 不工作 - 2

      关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion在首页我有:汽车:VolvoSaabMercedesAudistatic_pages_spec.rb中的测试代码:it"shouldhavetherightselect"dovisithome_pathit{shouldhave_select('cars',:options=>['volvo','saab','mercedes','audi'])}end响应是rspec./spec/request

    4. ruby - 即使失败也继续进行多主机测试 - 2

      我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r

    5. ruby-on-rails - Nokogiri:使用 XPath 搜索 <div> - 2

      我使用Nokogiri(Rubygem)css搜索寻找某些在我的html里面。看起来Nokogiri的css搜索不喜欢正则表达式。我想切换到Nokogiri的xpath搜索,因为这似乎支持搜索字符串中的正则表达式。如何在xpath搜索中实现下面提到的(伪)css搜索?require'rubygems'require'nokogiri'value=Nokogiri::HTML.parse(ABBlaCD3"HTML_END#my_blockisgivenmy_bl="1"#my_eqcorrespondstothisregexmy_eq="\/[0-9]+\/"#FIXMEThefoll

    6. ruby - 我可以将我的 README.textile 以正确的格式放入我的 RDoc 中吗? - 2

      我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西:

    7. ruby - 继续,未定义 callcc 方法 - 2

      我想学习一些关于Continuation的知识,使用callcc方法从一些文章中键入几个示例,但我遇到了错误:NoMethodError:undefinedmethod`callcc'formain:Objectfrom(pry):2:in`'没有文章提到包含延续库。那么如何解决这个问题呢?谢谢编辑:ruby1.9.2p290(2011-07-09修订版32553)[x86_64-linux] 最佳答案 您需要要求“继续”。require'continuation' 关于ruby-继续,

    8. ruby - 安装libv8(3.11.8.13)出错,Bundler无法继续 - 2

      运行bundleinstall后出现此错误:Gem::Package::FormatError:nometadatafoundin/Users/jeanosorio/.rvm/gems/ruby-1.9.3-p286/cache/libv8-3.11.8.13-x86_64-darwin-12.gemAnerroroccurredwhileinstallinglibv8(3.11.8.13),andBundlercannotcontinue.Makesurethat`geminstalllibv8-v'3.11.8.13'`succeedsbeforebundling.我试试gemin

    9. ruby-on-rails - 没有参数的 `<<`(小于两倍)是什么意思? - 2

      我在一个我想在formtasticGem中覆盖的方法中找到了这个。该方法如下所示:defto_htmlinput_wrappingdohidden_field_html是什么意思?在第三行做什么?我知道它对数组有什么作用,但在这里我不知道。 最佳答案 你可以这样读:hidden_field_htmllabel_with_nested_checkbox是连接到hidden_​​field_html末尾的参数-为了“清晰”,他们将其分成两行 关于ruby-on-rails-没有参数的`

    10. ruby-on-rails - 使用包含多个关联和单独的条件 - 2

      我的Gallery模型中有以下查询:media_items.includes(:photo,:video).rank(:position_in_gallery)我的图库模型有_许多媒体项,每个都有一个照片或视频关联。到目前为止,一切正常。它返回所有media_items包括它们的photo或video关联,由media_item的position_in_gallery属性排序。但是我现在需要将此查询返回的照片限制为仅具有is_processing属性的照片,即nil。是否可以进行相同的查询,但条件是返回的照片等同于:.where(photo:'photo.is_processingIS

    随机推荐