动态地将文本继续分成单独的<p>段落?

时间:2015-12-17 12:03:44

标签: javascript jquery html css

以下fiddle允许将文字粘贴到<textarea>并生成相等的段落,动态<p>由相同数量的字符组成

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

如果可以提供新的Fiddle,我将非常感激,因为我还不熟悉编码。再一次,小提琴可以找到here

更新:一旦生成了段落,用户是否可以按Enter键,如果可能,将其内容无缝移动到下面的段落中?并且当按下退格按钮时应用同样的内容,以使内容向上移动到上一段?发生的问题是,当按下回车时,文本似乎由于css中的溢出属性而隐藏文本。

&#13;
&#13;
$(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);
&#13;
@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;
}
&#13;
<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>
&#13;
&#13;
&#13;

7 个答案:

答案 0 :(得分:6)

  

用户可以按Enter键并将该内容向下移动   下一个现有段落,同时仍保留现有段落   动态自动格式化

如果我理解正确,你想要的是,一旦文本被分成段落,然后用户将一些文本添加到其中一个并按 Enter ,那么剩余的文本应该流动进入下一段,像以前一样分发溢出的文本。

类似地,当用户在段落的开头按 BackSpace 时,文本再次返回上一段,溢出的文本与之前的步骤一样分配到其他段落。

作为一种算法,你需要的是这样的东西:

  1. 将初始文本划分为相等的块并分发到根据需要动态创建p的段落。
  2. keyup元素
  3. 上收听p事件
  4. 如果按下键是 Enter
    • 3.1从按下 Enter 的位置提取剩余文本
    • 3.2从下面所有段落中提取文本,前面加上上面提取的溢出文本
    • 3.3删除下一个所有段落并像在步骤1中一样分发组合文本
  5. 如果按下键是 BackSpace
    • 4.1检查它是否在段落的开头,如果有前一段
    • 4.2提取段落的文本并附加到下一段所有段落的文本
    • 4.3删除包括当前段落在内的所有段落,并将提取的文本附加到前一段。
    • 4.4像在步骤1中一样分发合并文本
  6. 使用这种粗略的算法,您可以开始编码,看起来像这样:

    注1 :这是所有JavaScript,没有jQuery。
    注意2 :这过于简化,您需要进一步优化并解决所有边缘情况。

    缓存必需元素并绑定事件处理程序:

    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个字符来分割此示例的文字。此外,这不会处理空格,并将分隔两者之间的单词。您需要在代码中执行此操作。 (#见下面的编辑)

    用于捕获输入 keycode 13 )和 BackSpace keycode 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 < / em>),删除该节点,在此之后添加所有段落的剩余文本,并删除剩余的段落。

    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;
    }
    

    总而言之,这是一个有效的片段:

    <强>段:

    &#13;
    &#13;
    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;
    }
    &#13;
    * { 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; }
    &#13;
    <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>
    &#13;
    &#13;
    &#13;

    和你一起玩的小提琴:

    小提琴:https://jsfiddle.net/abhitalks/jwnnn5oy/

    编辑1:

    修正了在字边界处断开的正则表达式。还添加了相同的非正则程序过程代码(在Op的原始代码中),以演示如何将其他代码段纳入其中并将其集成到其中的Op

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

    更改集:添加了功能splitText

    编辑2:

    根据您的评论,如果您希望在用户输入时自动进行重新分发...那么您需要计算该段落中文本的长度,看看是否超过了您的块大小。如果是,则从该段开始重新分配。对退格做反向。

    我最初发布的解决方案是为了满足您的要求,当用户按下任何文本之间进行输入以断开并将其分发到后续段落时。我不建议自动将其作为用户类型执行,因为更改对用户来说太过于刺激。

    代码段2:

    &#13;
    &#13;
    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;
    }
    &#13;
    * { 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; }
    &#13;
    <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>
    &#13;
    &#13;
    &#13;

    小提琴2:https://jsfiddle.net/abhitalks/hvhr4ds8/

    注意5 :在小提琴中首先按Enter键以打破其间的一些文本,以便您能够看到重新分配如何发生当你输入。另外,请注意,由于不会破坏单词的逻辑,在重新分发之前需要多一些字符。

    更改集:添加了功能redistributeAuto

答案 1 :(得分:1)

D3实际上非常适合这种情况。如果我理解正确,<p>元素的添加和删除应该在编辑时自然出现并消失。

虽然有点粗糙,但在下面的示例中,检测到了一个新段落&#39;插入两个新行后。 <textarea>值符合该条件的.split(),并应用于右侧<div>的{​​{1}}数组。因此,对于数据中的每个元素,我们只需输入/ exit / update data()元素。我们在编辑文本的过程中得到了很好的简单添加和删除,而没有大量的DOM抖动。

通过一些改造,您可以将<p><textarea>合并到一个“wysiwyg”中。各种编辑...

&#13;
&#13;
<p>
&#13;
var text = '';
var break_char = '\n\n';
var editor = d3.select('.editor');
var output = d3.select('.output');

function input_handler () {

  text = editor.node().value;

  var paragraphs = output.selectAll('.paragraph')
    .data(text.split(break_char));

  paragraphs.enter()
    .append('p')
    .attr('class', 'paragraph')
    .style('opacity', 0);

  paragraphs.exit().remove();

  paragraphs
    .text(function (d) { return d })
    .transition()
    .duration(300)
    .style('opacity', 1);
}

editor.on('input', input_handler);
&#13;
body {
  vertical-align: top;
  height: 100%;
  background-color: #eee;
}
body * {
  box-sizing: border-box;
  font-family: arial;
  font-size: 0.8rem;
  margin: 0.5rem;
  padding: 0.5rem;
}
.input,
.output {
  display: inline-block;
  position: absolute;
  top: 0;
  height: auto;
}
.input {
  left: 0;
  right: 50%;
}
.output {
  left: 50%;
  right: 0;
}
.editor {
  display: inline-block;
  border: 0;
  width: 100%;
  min-height: 10rem;
  height: 100%;
}
.paragraph {
  background-color: white;
}
&#13;
&#13;
&#13;

答案 2 :(得分:1)

请检查fiddle。我添加了一些代码来监听Error: This socket has been ended by the other party元素按键并执行所需的文本操作。

<p>
$(function() {
  $("#Go").on('click', function() {
    var $p, a = [];
    var theText = $('textarea').val();
    var numberOfCharacters = 300;
    while (theText.length) {
      while (theText.length > numberOfCharacters &&
        theText.charAt(numberOfCharacters) !== ' ') {
        numberOfCharacters++;
      }

      $p = $("<p contenteditable class='text'>" + theText.substring(0, numberOfCharacters) + "<\/p>")
        .on('keydown', function(e) {
          var p = this;
          setTimeout(function() {
            if (e.which === 13) {
              var i;
              var k = $(p).html().split('<br>');
              if ((i = a.indexOf(p)) > -1 && a[i + 1])
                $(a[i + 1]).html(k.pop() + ' ' + $(a[i + 1]).html());

              $(p).html(k.join('<br>'));
            }
          });
        });

      a.push($p.get(0));

      $("#text_land").append("<br><\/br>", $p, "<br><\/br>");

      theText = theText.substring(numberOfCharacters);
      numberOfCharacters = 300;
    }
  })
})
$('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;
}
p {
  color: #000;
}
p {
  display: block;
  text-align: justify;
  border-width: 5px;
  font-size: 19px;
}
p {
  overflow: hidden;
  height: 300px;
  width: 460px;
  word-wrap: break-word;
}

答案 3 :(得分:1)

你可以查看Fiddle。嗯,我不确定这是不是你想要的。我只是在可编辑段落上添加事件来控制所需的输出。

&#13;
&#13;
$(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");
    }
  })
});
$(document).on('keyup', 'p.text', function(e) {
  if (e.keyCode == 13) {
    var extend = $(this).find("div").html();
    $(this).next().next().next().next().next().prepend(extend).focus();
    $(this).find("div").remove();
  }
});
$('select').on('blur keyup paste', function() {
  var targets = $('#text_land p'),
    property = this.dataset.property;
  targets.css(property, this.value);
}).prop('selectedIndex', 0);
(end);
&#13;
@media print {
  p {
    page-break-inside: avoid;
  }
}
p {
  position: relative;
}
@media print {
  .no-print,
  .no-print * {
    display: none !important;
  }
}
p {
  border-style: solid;
}
p {
  color: #000;
}
p {
  display: block;
  text-align: justify;
  border-width: 5px;
  font-size: 19px;
}
p {
  overflow: hidden;
  height: 300px;
  width: 460px;
  word-wrap: break-word;
}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.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>
&#13;
&#13;
&#13;

答案 4 :(得分:1)

将此事件绑定到您的每个段落

$('.text').bind("DOMSubtreeModified", function () {
                        var text = $(this).html();
                        var newLineIndex = text.indexOf('&nbsp;');
                        if (newLineIndex != -1) {
                            var currentP = text.substring(0, newLineIndex);
                            var newP = text.substring(newLineIndex + 11, text.length - 6);
                            $(this).html(currentP);
                            var nextElement = $(this).next();
                            if (nextElement != null) {
                                // append rest of line to next paragraph
                                nextPara = newP + nextElement.html();
                                nextElement.html(nextPara);
                            }
                            else {
                                // Else, create new paragraph
                                $(this).after('<br><\/br> <p contenteditable="true" class="text">' + newP + '</p>');
                            }
                        }
                    });

所以,你的整个代码应该是这样的,

$(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");

                    $('.text').bind("DOMSubtreeModified", function () {
                        var text = $(this).html();
                        var newLineIndex = text.indexOf('&nbsp;');
                        if (newLineIndex != -1) {
                            var currentP = text.substring(0, newLineIndex);
                            var newP = text.substring(newLineIndex + 11, text.length - 6);
                            $(this).html(currentP);
                            var nextElement = $(this).next();
                            if (nextElement != null) {
                                // append rest of line to next paragraph
                                nextPara = newP + nextElement.html();
                                nextElement.html(nextPara);
                            }
                            else {
                                // Else, create new paragraph
                                $(this).after('<br><\/br> <p contenteditable="true" class="text">' + newP + '</p>');
                            }
                        }
                    })
                }
            })
        })
        $('select').on('change', function () {
            var targets = $('#text_land p'),
                property = this.dataset.property;
            targets.css(property, this.value);
        }).prop('selectedIndex', 0);

请随意对此提出任何疑问。

答案 5 :(得分:0)

我认为CSS属性:white-space: pre-wrap可能是您正在寻找的内容:

https://jsfiddle.net/dbz3mwsb/1

答案 6 :(得分:0)

npm软件包paragraph-builder将连续文本分成均匀分布的段落,并且所有段落的字数大致相同。您可以定义段落的单词数。

  

此段落构建器节点脚本从连续文本生成段落。它输出一个文本,其中每个段落的大小大致相同,从而在文本中提供均匀的段落分布。它不会将数字拆分为“ 1.2”之类的数字。

有一个选项可以定义段落之间的分隔符,或者您可以将段落提取到一个字符串数组中,并从中应用html标签<p>。检查其文档以进一步澄清。