9

This is a continuation from my previous question.

I managed to fixed the "animation pauses every continuous keystrokes" by adding a condition e.which === 13, which detects and animate when the Enter key is pressed.


This is how the previous one works:

enter image description here

As you can see, after entering a newline then a continues of keystrokes, the animation lags, which means the animation executes on every keystrokes.


These is the modified one, that will only animate after the Enter is pressed:

enter image description here

It works smoothly (it lags a bit though during the recording).


These is how it works when deleting every character (not long press):

enter image description here

As you can see, it didn't animate well, because as you delete every character continuously, the animation pauses, just like the first attempt.


So what I'm trying to achieve now is the reverse one, animate smoothly after deleting a newline.

Here's a live code:

var kAnimationSpeed = 250;
var kPadding = 10;

$('div[contenteditable]').on('blur keyup paste input', function(e) {
  var styleElement = $(this).prev();

  var editorHeight = $(this).height();
  var styleElementHeight = styleElement.height();

  if (editorHeight !== styleElementHeight - kPadding * 2 && e.which === 13 || e.which === 8) {
    styleElement.stop().animate({ height: editorHeight + kPadding * 2 }, kAnimationSpeed);
  }
});
.autogrowWrapper {
  position: relative;
}

.autogrow {
  border: 1px solid rgb(0, 0, 0);
  height: 40px; /* line-height + 2 * padding */
}

div[contenteditable] {
  outline: none;
  line-height : 20px;
  position: absolute;
  top: 10px; /* padding */
  left: 10px; /* padding */
  right: 10px; /* padding */
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="autogrowWrapper">
  <div class="autogrow"></div>
  <div contenteditable="true"></div>
</div>

How can I achieve a smooth animation on deleting a newline, or how to animate only when a newline is deleted?

Community
  • 1
  • 1
Cookie Ninja
  • 1,156
  • 15
  • 29
  • 1
    In your `if` statement, try adding something like `if ( ! $(this).is(':animated') )` as the lag *could* be multiple calls to the animation, after first calling `stop()` etc. – adeneo Aug 09 '16 at 02:04
  • When I add that condition with `&&` operator, still the same. But when I tried `||` operator, it affects the enter process. Which means it works the same on the previous attempt. :v – Cookie Ninja Aug 09 '16 at 02:10
  • Maybe something like this http://jsfiddle.net/yfSUk/3/ but substitute left for top and when the value changes proceed w ur animation – Squirrl Aug 11 '16 at 04:41
  • http://stackoverflow.com/questions/13347471/caret-position-in-pixels-in-an-input-type-text-not-a-textarea – Squirrl Aug 11 '16 at 04:42
  • @Squirrl How to get the newline? – Cookie Ninja Aug 11 '16 at 04:51
  • @Zange-chan: I've changed the event from keyup to keydown. Then at the time of 'backspace' key stroke, which is e.which === 8, I reduced the animation time. In this way we can fine tune it. Have you tried that way? – JTheDev Aug 11 '16 at 05:53
  • http://ichord.github.io/Caret.js/ when the top changes trigger the animation – Squirrl Aug 11 '16 at 05:55
  • @JTheDev I have tried reducing the time, but I want to keep it that way. – Cookie Ninja Aug 11 '16 at 06:04

2 Answers2

6

I dont know if this works crossbrowser but Chrome adds <div>s for lines, so you can actually count them:

working fiddle

var kAnimationSpeed = 250;
var kPadding = 10;
var keyCodeDelete = 8;
var keyCodeReturn = 13;

$('div[contenteditable]').on('blur keyup paste', function(e) {
  var el = $(this);
  var styleElement = el.prev();
  var editorHeight = el.height();
  var styleElementHeight = styleElement.height();

  var divCount = parseInt(el.data('divcount') || 0 );
  var newDivCount = el.children('div').not(':contains("br")').length;
  var newLineDeleted = (divCount - 1 == newDivCount && e.which == keyCodeDelete);

  var heightCheck = (editorHeight !== styleElementHeight - kPadding * 2);
  var keyCodeCheck = (e.which === keyCodeReturn || e.which === keyCodeDelete);


  if (heightCheck && keyCodeCheck) {
    if (newLineDeleted) {
      $('body').append('<p>nl removed!</p>');
    }
    styleElement.stop().animate({ height: editorHeight + kPadding * 2 }, kAnimationSpeed);
  }

  el.data('divcount', newDivCount);
});

So you just have to adjust your animation :)

Alex
  • 9,911
  • 5
  • 33
  • 52
4

So I managed to solve the problem based on Alex' answer.

In his answer, it only works when pressing the Enter key. But my goal is use Shift + Enter. Pressing Enter will insert new <div> inside the contenteditable div, while the Shift + Enter inserts a new <br> instead of <div>. So I modify the code which will count <br>, stores the value to the data, and compares whether the current number of lines is less than the new number of lines, and also listens the Backspace key. I then replace the e.which === 8 with newLineDeleted variable to tell when a new line is deleted. And remove the input function so it will return the exact boolean value (because it seems not to work with the input function in it. I add cut and paste listener as well.

var kAnimationSpeed = 250;
var kPadding = 10;

$('div[contenteditable]').on('blur keyup paste', function(e) {
  var styleElement = $(this).prev();
  var editorHeight = $(this).height();
  var styleElementHeight = styleElement.height();
  
  var lineCount = parseInt($(this).data('linecount') || 0);
  var newLineCount = $(this).children('br').length;
  $(this).data('linecount', newLineCount);
  var newLineDeleted = (lineCount - 1 === newLineCount && e.which === 8);
  
  var cutPasteListener = (e.ctrlKey && e.which === 86 || e.which === 88);

  if (editorHeight !== styleElementHeight - kPadding * 2 && e.which === 13 || newLineDeleted || cutPasteListener) {
    styleElement.stop().animate({ height: editorHeight + kPadding * 2 }, kAnimationSpeed);
  }
});
.autogrowWrapper {
  position: relative;
}

.autogrow {
  border: 1px solid rgb(0, 0, 0);
  height: 40px; /* line-height + 2 * padding */
}

div[contenteditable] {
  outline: none;
  line-height : 20px;
  position: absolute;
  top: 10px; /* padding */
  left: 10px; /* padding */
  right: 10px; /* padding */
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="autogrowWrapper">
  <div class="autogrow"></div>
  <div contenteditable="true"></div>
</div>

Thanks.

Cookie Ninja
  • 1,156
  • 15
  • 29