1

I have a plugin that creates and, on an interval, populates a <p> with the content of a <textarea>. The plugin positions the <p> underneath the <textarea>, and styles the elements so that their "boxes" are identical. Additionally, the background and text of the <textarea> are defined as transparent so that the content of the <p> can be seen.

Ideally, the elements and their contents will mirror one another at all times. And in most cases, they do. However, when both elements are made to be scrollable, the dynamic breaks; this is due to a difference in the scrollHeight of the two elements (the scrollHeight of <textArea> is larger than that of the <p>)

Here is the code:

var $shadowParagraphObj = $("#shadowParagraph");
var $contentTextAreaObj = $("#contentTextArea").scroll(scrollShadowParagraph);

function scrollShadowParagraph(event)
{
  var textAreaScrollLeft = $contentTextAreaObj.scrollLeft();
  var textAreaScrollTop = $contentTextAreaObj.scrollTop();

  if($shadowParagraphObj.scrollLeft() != textAreaScrollLeft)
    $shadowParagraphObj.scrollLeft(textAreaScrollLeft)

  if($shadowParagraphObj.scrollTop() != textAreaScrollTop)
    $shadowParagraphObj.scrollTop(textAreaScrollTop)
}

var intervalId = setInterval(function(){$shadowParagraphObj.html($contentTextAreaObj.val())}, 100);
#containerDiv {
  position: relative;
  left: 50%;
  margin-left: -250px;
  width: 510px;
  height: 200px;
}

#shadowParagraph, #contentTextArea {
  width: inherit;
  height: inherit;
  overflow: scroll !important;
  padding: 4px;
  border : none;
  outline: none;
  margin: 0px;
  white-space: pre-wrap;
  word-wrap: pre-wrap;
  font: 1em Arial, sans-serif;
}

#shadowParagraph {
  position: absolute;
  z-index: 0;
  background: white;
  color: blue;
}

#contentTextArea {
  position: relative;
  z-index: 1;
  background: transparent;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='containerDiv'>
  <p id='shadowParagraph'></p>
  <textarea id='contentTextArea'></textarea>
</div>

Overflowing the <textarea> should produce the issue (the text of the <textarea> has been given color to make the issue easy to see).

Have I forgot to declare some properties that are causing this discrepancy between scrollHeight values? If so, what are they and how should I declare them? If not, is there any way to ensure that the scrollHeight of the two elements is equal at all times?

Braiam
  • 1
  • 11
  • 47
  • 78
Kevin
  • 2,617
  • 29
  • 35
  • 2
    Please include the code in the question. – Michał Perłakowski May 23 '16 at 18:51
  • It looks as though it's not getting the height of line breaks until a character has been entered... looking into it atm – Grant Mooney May 23 '16 at 18:55
  • Gothdo - checkout the jsFiddle – Grant Mooney May 23 '16 at 18:55
  • 2
    @GrantMooney Did you heard about [mcve]? – Michał Perłakowski May 23 '16 at 18:57
  • @Gothdo yes thank you, feel free to suggest an edit – Grant Mooney May 23 '16 at 19:01
  • 1
    @Gothdo: The jsFiddle link was included in lieu of code because this is a question which involves Javascript, HTML, and CSS, making the site the most efficient way to showcase the issue. The link you provided **does not** state that including code in a question is *mandatory*, and as such your downvote and request to close this question can be viewed as abuses of said facilities. – Kevin May 23 '16 at 19:13
  • 1
    @Kevin, I am getting closer to a solution by using `.replace(/\n\r?/g, '
    ')` on the retrieved values to convert line breaks into html line breaks. please see this [jsFiddle example](https://jsfiddle.net/31kyx3vo/16/)
    – Grant Mooney May 23 '16 at 19:32
  • @GrantMooney: Your hunch was correct! Upon scrolling up and down, the problem corrects itself, but the initial discrepancy is still there, which is interesting since the "update" runs on an interval... – Kevin May 23 '16 at 19:37
  • Glad to be of help! I have provided an extended answer below – Grant Mooney May 23 '16 at 19:58

1 Answers1

1

Okay so using .replace(/\n\r?g, '<br />') to convert updated values, your line breaks will be converted into html line breaks. Additionally, html tends to ignore lone <br /> line breaks, so you will want to add an additional <br /> to the value to ensure the last line break is rendered.

Put together this would look something like:

var textAreaHTML = $myTextArea.val().replace(/\n\r?g, '<br />')+'<br />';

Additionally, I would recommend updating your textarea values AND scroll position on the .keyup() event, .keypress() event, or both events using .on('keyup keypress', function() {...}).

To see this in action check out this jsFiddle example

Grant Mooney
  • 390
  • 8
  • 17
  • Great, but I need to be able to detect *all* changes in the textarea, not just those that are the result of keyboard strokes. For example, there is no event fired when text is pasted via context-menu; the only way to handle changes like that (that I know of) is to use an interval function – Kevin May 23 '16 at 20:35
  • 1
    Ah, you may wanna take a look at [this question](http://stackoverflow.com/questions/4702814/detecting-a-context-menu-paste-in-the-browser-with-jquery) – Grant Mooney May 23 '16 at 21:49