Подтвердить что ты не робот

Продолжать текст в отдельные параграфы <p>динамически?

Следующий Fiddle позволяет вставить текст в <textarea> и сгенерирован в равные абзацы динамически <p> состоящий из того же количества символов.


Возникает проблема; текст из предыдущих динамически сгенерированных абзацев <p> переполняется в каждом теге и не переходит к следующему динамическому абзацу. Таким образом, можно ли пользователю нажать ввод и переместить этот контент вниз в следующий существующий абзац, сохраняя при этом существующее форматирование динамически и автоматически?

Если бы был предоставлен новый Fiddle, это было бы очень признательно, поскольку я все еще новичок в кодировании. Еще раз, Fiddle можно найти здесь.

Обновление: Возможно ли, как только будут созданы абзацы, чтобы пользователь нажал кнопку ввода и, если возможно, легко переместил свой контент в нижний абзац? А также для того, чтобы применить, когда нажата кнопка backspace, чтобы содержимое переместилось вверх в вышеприведенный абзац? Возникающая проблема заключается в том, что текст при нажатии enter, кажется, скрывает текст из-за свойства переполнения в css.


$(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>
4b9b3361

Ответ 1

чтобы пользователь мог нажать Enter и переместить этот контент вниз следующий существующий пункт, сохраняя при этом существующие форматирование динамически и автоматически

Если я правильно вас понимаю, то, что вы хотите, состоит в том, что после того, как текст будет разделен на абзацы, а затем пользователь добавит текст в один из них и нажимает 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. Это слишком упрощено, вам нужно будет еще больше оптимизировать и выработать все краевые случаи.

Кэшировать требуемые элементы и связывать обработчики событий:

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 (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, извлеките текст после последнего дочернего элемента текущего абзаца (contenteditable будет выдавать div при нажатии Enter), удалите этот node, добавьте оставшийся текст всех абзацев, которые после этого и удалите оставшиеся абзацы.

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>

Ответ 2

Очень просто, если я правильно вас понимаю.

$(function() {
    $("#Go").on('click', function() {
        var theText = $('textarea').val();
        var paragraphs = theText.split('\n\n');
        $("#text_land").html('');
        paragraphs.forEach(function(paragraph) {
          var lines = paragraph.split('\n');
          $('<p class="text" contenteditable />').html(lines.join('<br>')).appendTo("#text_land");
        });
    })
})
$('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>

Ответ 3

D3 на самом деле вполне подходит для этого. Если вы правильно поняли, добавление и удаление элементов <p> должны появляться и исчезать естественным образом при редактировании.

Это немного грубо, но в приведенном ниже примере новый абзац "обнаружен" после вставки двух новых строк. Значение <textarea> соответствует .split() по этим критериям и применяется к <div> справа как массив data(). Поэтому для каждого элемента данных мы просто вводим/выходим/обновляем элементы <p>. Мы получаем приятные и легкие дополнения и удаления, когда мы редактируем текст без большого количества промахов DOM.

С некоторой переработкой вы могли бы, вероятно, объединить <textarea> и <p> в редактор wysiwyg...

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);
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;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div>
  <div class='input'>
    <textarea class='editor' placeholder='write away...'></textarea>
  </div>
  <div class='output'></div>
</div>

Ответ 4

Пожалуйста, проверьте fiddle. Я добавил код для прослушивания клавиши <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;
}
<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>

Ответ 5

можете проверить это Fiddle. Ну, я не уверен, что это то, что ты хотел. Я просто добавляю событие в редактируемый абзац для управления желаемым выходом.

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

Ответ 6

Привяжите это событие к каждому абзацу,

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

Пожалуйста, не стесняйтесь задавать любые сомнения по этому поводу.

Ответ 7

Я думаю, что свойство CSS: white-space: pre-wrap может быть тем, что вы ищете:

https://jsfiddle.net/dbz3mwsb/1

Ответ 8

Если я правильно понял ваш вопрос, вы можете добавить что-то вроде этого:

$('#text_land').keyup(function(e) {
  if(e.keyCode == '13') {
    $(this).append("<br><br><p contenteditable='true' class='text'></p>");
    $('p.text:last-child').focus();
  }
});

Затем, когда вы печатаете, он будет генерировать новые поля "на лету" всякий раз, когда пользователь нажимает "Enter". Вот полный пример: https://jsfiddle.net/2hcfbp2h/6/

Ответ 9

Построитель абзацев пакета npm разбивает непрерывный текст на равномерно распределенные абзацы с одинаковым количеством слов. Вы можете определить количество слов для абзацев.

Этот скрипт узла построителя абзацев генерирует абзацы из непрерывного текста. Он выводит текст, в котором размер каждого абзаца примерно одинаков, обеспечивая равномерное распределение абзацев в тексте. Он не разбивает текст на числа, такие как "1.2".

Существует возможность определить символ разрыва между абзацами, или вы можете извлечь абзацы в массив строк, из которого вы можете применить html-тег <p>. Проверьте его документацию для дальнейшего уточнения.