0

I am trying to make an input, where the user types a text. What I want to do is: After the user enters 10 characters, the next characters they type will be another color. Does anyone know how I can do?

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <input type="text" maxlength="255">

  <script>
    let input = document.querySelector("input");

    input.addEventListener("keyup", function() {
      if(input.value.length > 10) {
        console.log(input.value.substring(10));
      }
    })
    
  </script>
</body>
</html>

Following is an example code, I want to get this text after the user type 10 characters and change their color. So what I researched one way to do is using "contenteditable div", but how?

Julio
  • 139
  • 1
  • 1
  • 8

2 Answers2

2

Here is a fully implemented example. The strategy is similar to the one outlined by chris' answer. Basically, we use a contenteditable div to allow the user to enter some content. Once the content is over 10 characters, we use a span to style all subsequent characters.

One issue is that changing the HTML of the editable div (which includes appending a span) will cause the cursor to reset to the beginning. To get around this issue, I used code from this answer to set the cursor back to the end.

<input id='test-value' type='hidden'>
<div id='test' style="border:1px solid" contenteditable="true">
<script>
 let input = document.querySelector("#test");
 
 input.addEventListener("keypress", function(e) {
  if (e.ctrlKey) return;
  
  let value = input.innerText;
  if (value.length > 10) {
   let firstTen = value.substring(0, 10);
   let remainingText = value.substring(10);

   // The text may contain HTML, which could lead to an XSS vulnerability.
   // To avoid this we create a span and set the span's innerText instead of
   // modifying the input's innerHTML directly.
   let span = document.createElement("span");
   span.style.color = "red";
   span.innerText = remainingText;
   input.innerText = firstTen;
   input.appendChild(span);

   input.focus();

   setEndOfContenteditable(input);                    
  }
  document.querySelector("#test-value").value = input.innerText;
 });

 // From https://stackoverflow.com/a/3866442/11981207
 function setEndOfContenteditable(contentEditableElement)
 {
  var range,selection;
  if(document.createRange)//Firefox, Chrome, Opera, Safari, IE 9+
  {
   range = document.createRange();//Create a range (a range is a like the selection but invisible)
   range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range
   range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
   selection = window.getSelection();//get the selection object (allows you to change selection)
   selection.removeAllRanges();//remove any selections already made
   selection.addRange(range);//make the range you have just created the visible selection
  }
  else if(document.selection)//IE 8 and lower
  { 
   range = document.body.createTextRange();//Create a range (a range is a like the selection but invisible)
   range.moveToElementText(contentEditableElement);//Select the entire contents of the element with the range
   range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
   range.select();//Select the range (make it the visible selection
  }
 }
</script>

In your comment you mentioned that you cannot modify the HTML directly. As a workaround, you can use JavaScript to convert your input to a hidden input, and insert a contenteditable div after it.

<!-- Your original HTML, untouched -->
<input type="text" maxlength="255">

<script>
 let input = document.querySelector("input");
 input.type = "hidden";
 
 let editableDiv = document.createElement("div");
 editableDiv.contentEditable = true;
 editableDiv.style.border = "1px solid black"; 
 
 input.parentNode.insertBefore(editableDiv, input.nextSibling);
 
 editableDiv.addEventListener("keypress", function(e) {
  if (e.ctrlKey) return;
  
  let value = editableDiv.innerText;
  if (value.length > 10) {
   let firstTen = value.substring(0, 10);
   let remainingText = value.substring(10);

   // The text may contain HTML, which could lead to an XSS vulnerability.
   // To avoid this we create a span and set the span's innerText instead of
   // modifying the editableDiv's innerHTML directly.
   let span = document.createElement("span");
   span.style.color = "red";
   span.innerText = remainingText;
   editableDiv.innerText = firstTen;
   editableDiv.appendChild(span);

   editableDiv.focus();

   setEndOfContenteditable(editableDiv);                    
  }
  input.value = editableDiv.innerText;
 });

 // From https://stackoverflow.com/a/3866442/11981207
 function setEndOfContenteditable(contentEditableElement)
 {
  var range,selection;
  if(document.createRange)//Firefox, Chrome, Opera, Safari, IE 9+
  {
   range = document.createRange();//Create a range (a range is a like the selection but invisible)
   range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range
   range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
   selection = window.getSelection();//get the selection object (allows you to change selection)
   selection.removeAllRanges();//remove any selections already made
   selection.addRange(range);//make the range you have just created the visible selection
  }
  else if(document.selection)//IE 8 and lower
  { 
   range = document.body.createTextRange();//Create a range (a range is a like the selection but invisible)
   range.moveToElementText(contentEditableElement);//Select the entire contents of the element with the range
   range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
   range.select();//Select the range (make it the visible selection
  }
 }
</script>
Kei
  • 1,026
  • 8
  • 15
  • @Julio just tested it, and it looks like the code here, https://stackoverflow.com/a/3866442/11981207, works as well. Since it is significantly shorter, I've replaced the code in my example with this one. – Kei Sep 19 '19 at 03:19
  • I have a problema @Kei. The problem is i can't change the html, having to use an input that is wrapped in a span. Do you know how I can do it this way? – Julio Sep 19 '19 at 06:41
  • I can't think of any good ways to get around it. It's not possible to have two different font colors for a given input element. Also, the input element does not support subelements. I take it that you can't change the HTML, but you can run scripts? If so, I suppose one workaround would be to modify the HTML using a script. For example, you could write a script that converts the text input into a hidden input. The script could then append a contenteditable div after the input and the rest would be the same. – Kei Sep 19 '19 at 06:57
  • @Julio, I edited the answer to show an example of the workaround. – Kei Sep 19 '19 at 07:05
0

How about a combination of a hidden input, the onInput event handler, content editable, and element.html(). For each character typed into the contenteditable div, save that char to a hidden input then set the innerHTML of the contenteditable div to the first 10 chars of the current value of the hidden input + a span with a class name wrapping the remaining chars.

chris
  • 6,653
  • 6
  • 41
  • 54