Change color of specific words in textarea
Asked Answered
U

9

30

I'm building a Sql query builder and would like to change the text color of a word in a textarea when the user types in words like SELECT, FROM, WHERE.

I've searched around a bit and beyond this (https://jsfiddle.net/qcykvr8j/2/) I unfortunately do not come any further.

Example code

HTML:

<textarea name="query_field_one" id="query_field_one" onkeyup="checkName(this)"></textarea>

JS:

    function checkName(el)
    {
    if (el.value == "SELECT" || 
    el.value == "FROM" || 
    el.value == "WHERE" || 
    el.value == "LIKE" || 
    el.value == "BETWEEN" || 
    el.value == "NOT LIKE" || 
    el.value == "FALSE" || 
    el.value == "NULL" || 
    el.value == "TRUE" || 
    el.value == "NOT IN")
    {
      el.style.color='orange'

    }
    else {
      el.style.color='#FFF'

    }
  }

JSFiddle:

https://jsfiddle.net/qcykvr8j/2/

But this example deletes the color when I type further.

What I want is this:

Right way

I've tried something with Keyup in combination with Contains in jQuery but that did not result in much.

Keyup: https://api.jquery.com/keyup/

Contains: https://api.jquery.com/contains-selector/

I hope someone can help me with an example or sites where I can find more information .

Regards, Jens

Unhallowed answered 10/5, 2016 at 12:49 Comment(6)
You are changing the element color el.style.color thats why passed it isn't working. You need to use CKEditor type of thing. Doing this manually require handle words into spans or div 's with custom style tagsMethodism
Yeah, I know why the example is not working. The example is just for some more information. I thought it was maybe easy to make it with Jquery but it is indeed too hard I think.Unhallowed
Have you considered using different style classes, then do a search/replace for "SELECT" replace with "<span class='orange'>SELECT</span>".Ranaerancagua
You're checking the entire value of the text area each time. Your code would only work if the contents of the textarea contain only one of those words and nothing else. You could use jQuery .split() to grab each word into an array and compare; probably a better way, just first thing that came to mind.Isogamy
Yes, only it seemed not very useful to me..Unhallowed
@Isogamy Good advice, I'm gonna try it.Unhallowed
S
45

You can't change the colours of words in a <textarea>, but you can use the contenteditable attribute to make a <div>, <span>, or <p> look like a <textarea>.

To do this you can use a JavaScript plugin, but if you want to create a new one, the code below may help you.

For this purpose, you need to get any word in the text. Then check that if it's a SQL keyword.

// SQL keywords
var keywords = ["SELECT","FROM","WHERE","LIKE","BETWEEN","NOT LIKE","FALSE","NULL","FROM","TRUE","NOT IN"];
// Keyup event
$("#editor").on("keyup", function(e){
  // Space key pressed
  if (e.keyCode == 32){
    var newHTML = "";
    // Loop through words
    $(this).text().replace(/[\s]+/g, " ").trim().split(" ").forEach(function(val){
      // If word is statement
      if (keywords.indexOf(val.trim().toUpperCase()) > -1)
        newHTML += "<span class='statement'>" + val + "&nbsp;</span>";
      else
        newHTML += "<span class='other'>" + val + "&nbsp;</span>"; 
    });
    $(this).html(newHTML);

    // Set cursor postion to end of text
    var child = $(this).children();
    var range = document.createRange();
    var sel = window.getSelection();
    range.setStart(child[child.length-1], 1);
    range.collapse(true);
    sel.removeAllRanges();
    sel.addRange(range);
    this.focus();
  }
});
#editor {
    width: 400px;
    height: 100px;
    padding: 10px;
    background-color: #444;
    color: white;
    font-size: 14px;
    font-family: monospace;
}
.statement {
    color: orange;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="editor" contenteditable="true"></div>
Suffolk answered 11/5, 2016 at 10:58 Comment(15)
Wow! This is actually a great solution its a bit difficult with posting the data but I will fix that with Ajax and I can remove the html tags with strip_tag(); Thanks for your response!Unhallowed
@VenkatSoma No, input and textarea can't have child element and style for content. The target element should accept child element.Suffolk
this code seems to remove line breaks, is there a tweak that would allow it to keep the line breaks. I am using it for a similar setup and when I press enter it removes the line break and puts it all back on one line.Jannette
@Suffolk your solution is great! But it doesn't seem to highlight multi word like 'not in' as you mentioned in your switch cases. Any idea why?Zaneta
@SouvikRay Use not and in keyword separately in switchSuffolk
@Suffolk ok got it!Zaneta
@Suffolk hey! So I was working on your code and it works fine except a situation where I write a sentence and I make a typo such that I attach two words together For example Hey select mefrom here As you can see mefrom is two words. Now I place the cursor after e and hit a space. But on hitting space, it takes me to the end of sentence. I tried tweaking your cursor positioning code but no joy so far. Any idea how can I do this?Zaneta
@SouvikRay Just remove keydown eventSuffolk
@Suffolk you are awesome! Thanks buddy for your time!Zaneta
It goes back to the end of the code when I write qsido at the beginning of it.Vertex
I really like this code, can we remove the space key press to see the actual effect? Meaning, I have a div tag where the data is auto populated from another function. Want to use your JS code for highlighting.Reluct
@Reluct Remove keyup event handler of code and run it after your function.Suffolk
you legit can do text color change in textareas. If you don't know how to do something you can not just straight up say "you can not do this" because it is 100% possible to change textareas text color with specific words. Codemirror even uses textareas @SuffolkIndemonstrable
@Indemonstrable No, codemirror also used contenteditableSuffolk
@Suffolk I see now that they overlay it with a DIV but do you know how to do the overlay???Indemonstrable
S
8

JS FIDDLE CODE

HTML-

<div id="board" class="original" contenteditable="true"></div>
<div id="dummy" class="original"></div>

CSS-

.original {
   position:absolute;width: 50%; margin: 0 auto; padding: 1em;background: #fff;height:100px;margin:2px;border:1px solid black;color:#fff;overflow:auto;
}

#dummy{
  color:black;
}
#board{
  z-index:11;background:transparent;color:transparent;caret-color: black;
}
.original span.highlighted {
    color:red;
}

JAVASCRIPT -

var highLightedWord = ["select","insert","update","from","where"];
var regexFromMyArray = new RegExp(highLightedWord.join("|"), 'ig');
$('#board').keyup(function(event){
 document.getElementById('dummy').innerHTML = $('#board').html().replace(regexFromMyArray,function(str){
 return '<span class="highlighted">'+str+'</span>'
 })
})
var target = $("#dummy");
  $("#board").scroll(function() {
    target.prop("scrollTop", this.scrollTop)
          .prop("scrollLeft", this.scrollLeft);
  });
Sadiras answered 14/2, 2019 at 9:57 Comment(1)
Nice solution! This is much better than the other ones.Pyramidal
R
4

With Vanilla JS, you can do it as:

    // SQL keywords
    var keywords = ["SELECT", "FROM", "WHERE", "LIKE", "BETWEEN", "UNION", "FALSE", "NULL", "FROM", "TRUE", "NOT", "ORDER", "GROUP", "BY", "NOT", "IN"];
    // Keyup event
    document.querySelector('#editor').addEventListener('keyup', e => {
    // Space key pressed
    if (e.keyCode == 32) {
        var newHTML = "";
        // Loop through words
        str = e.target.innerText;
        chunks = str
          .split(new RegExp(
            keywords
              .map(w => `(${w})`)
              .join('|'), 'i'))
          .filter(Boolean),
        markup = chunks.reduce((acc, chunk) => {
          acc += keywords.includes(chunk.toUpperCase()) ?
          `<span class="statement">${chunk}</span>` :
          `<span class='other'>${chunk}</span>`
          return acc
        }, '')      
        e.target.innerHTML = markup;

        // Set cursor postion to end of text
        //    document.querySelector('#editor').focus()
        var child = e.target.children;
        var range = document.createRange();
        var sel = window.getSelection();
        range.setStart(child[child.length - 1], 1);
        range.collapse(true);
        sel.removeAllRanges();
        sel.addRange(range);
        this.focus();
            
        }
    });
        #editor {
            width: 400px;
            height: 100px;
            padding: 10px;
            background-color: #444;
            color: white;
            font-size: 14px;
            font-family: monospace;
        }
        .statement {
            color: orange;
        }
<div id="editor" contenteditable="true"></div>
Ras answered 24/9, 2020 at 20:41 Comment(1)
I really like this code, can we remove the space key press to see the actual effect? Meaning, I have a div tag where the data is auto populated from another function. Want to use your JS code for highlighting.Reluct
K
2

I couldn't do something about caret so i borrow other people's work and it made code kinda huge. i don't know how fast it is but it works well :T

codes that i borrowed:

https://jsfiddle.net/nrx9yvw9/5/

Get a range's start and end offset's relative to its parent container

you can run with no jquery:

 //(sorry for the grammer mistakes)

/*Caret function sources: https://jsfiddle.net/nrx9yvw9/5/ && https://mcmap.net/q/83470/-get-a-range-39-s-start-and-end-offset-39-s-relative-to-its-parent-container/4812022#4812022*/
function createRange(e,t,n){if(n||((n=document.createRange()).selectNode(e),n.setStart(e,0)),0===t.count)n.setEnd(e,t.count);else if(e&&t.count>0)if(e.nodeType===Node.TEXT_NODE)e.textContent.length<t.count?t.count-=e.textContent.length:(n.setEnd(e,t.count),t.count=0);else for(var o=0;o<e.childNodes.length&&(n=createRange(e.childNodes[o],t,n),0!==t.count);o++);return n}function getCurrentCaretPosition(e){var t,n=0,o=e.ownerDocument||e.document,a=o.defaultView||o.parentWindow;if(void 0!==a.getSelection){if((t=a.getSelection()).rangeCount>0){var r=a.getSelection().getRangeAt(0),c=r.cloneRange();c.selectNodeContents(e),c.setEnd(r.endContainer,r.endOffset),n=c.toString().length}}else if((t=o.selection)&&"Control"!=t.type){var i=t.createRange(),g=o.body.createTextRange();g.moveToElementText(e),g.setEndPoint("EndToEnd",i),n=g.text.length}return n}function setCurrentCaretPosition(e,t){if(t>=0){var n=window.getSelection();range=createRange(e,{count:t}),range&&(range.collapse(!1),n.removeAllRanges(),n.addRange(range))}}
/*Caret functions end*/


/*
 * -> required | [...,...] -> example | {...} -> value type | || -> or 

  id:         Position of words for where they should be colored  [undefined,0,1,...] {int||string}
  color:      Color for words  [aqua,rgba(0,255,0,1),#ff25d0] {string}
  fontStyle:  Font style for words  [italic,oblique,normal] {string}
  decoration: Text decoration for words  [underlined,blink,dashes] {string}
* words:      Words that should be colored  {array}
*/
var keywords = [
   {
      color: "orange",
      words: [
         "SELECT",
         "FROM",
         "WHERE",
         "LIKE",
         "BETWEEN",
         "NOT",
         "FALSE",
         "NULL",
         "TRUE",
         "IN",
      ],
   },
   {
      id: 0,
      color: "red",
      fontStyle: "italic",
      decoration: "underline",
      words: ["TEST"],
   },
];

//defining node object as "editor"
var editor = document.getElementById("editor");

//listening editor for keyup event
editor.addEventListener("keyup", function (e) {
   // if ctrl or alt or shift or backspace and keyname's length is not 1, don't check
   if( e.ctrlKey || e.altKey || ( e.key.length - 1 && e.key != "Backspace" ) || ( e.shiftKey && e.char ) ) return;

   //getting caret position for applying it in the end, because after checking and coloring done; it's gonna be at the beginning.
   pos = getCurrentCaretPosition(this);

   
   text = this.innerText; //getting input's just text value
   words = text.split(/\s/gm); //splitting it from all whitespace characters

   for (var i = 0; i < keywords.length; i++)
      for (var n = 0; n < words.length; n++) {
         //looks for is word in our "keywords"' object and check's position if it's id entered
         if (keywords[i].words.indexOf(words[n].toUpperCase().trim()) > -1 && (keywords[i].id >= 0 ? keywords[i].id == n : true) )
            //applys options to word
            words[n] = `<span style="color:${ keywords[i].color ?? "white" };font-style:${ keywords[i].fontStyle ?? "normal" };text-decoration:${ keywords[i].decoration ?? "normal" }">${words[n]}</span>`;
      }

   //joining array elements with whitespace caracter and apply it to input
   this.innerHTML = words.join("&nbsp;");
   //restoring caret position
   setCurrentCaretPosition(this, pos);
});
    #editor {
   width: 400px;
   height: 100px;
   padding: 10px;
   background-color: #444;
   color: white;
   font-size: 14px;
   font-family: monospace;
   font-weight: normal;
   caret-color: white;
}
<div id="editor" spellcheck="false" contenteditable="true"></div>
Knawel answered 7/7, 2021 at 14:29 Comment(2)
I mean... it works! Although I do recommend formatting your code so it becomes easier for others to read. Online tools like prettier playground help.Showoff
@MingyeWang oh, i really forgot about formatting.. thanks for advice.Knawel
E
2

the content editable div have some serius flaws, and what worked the best for me is overlaying a div on a textarea with only it's text hidden

updateScreen($("#in").val());
$("#in").on("keydown", function(e) {
  setTimeout(() =>{
    updateScreen($(this).val());
  },0)
})
function updateScreen(text)
{
  $("#out").html(colorize(text.replace(/\n/g, "<br>").replace(/\t/g,"&#9;")));
}
$("#in").on('scroll', function(){
  // set out to be the same as in
  $("#out").css({top:-$(this).scrollTop()+"px"});   
});


function colorize(text)
{
var keywords = ["SELECT","FROM","WHERE","LIKE","BETWEEN","NOT LIKE","FALSE","NULL","FROM","TRUE","NOT IN"];
  for(const keyword of keywords)
  {
    text = text.replaceAll(keyword,`<span style="color:orange">${keyword}</span>`)
    text = text.replaceAll(keyword.toLowerCase(),`<span style="color:orange">${keyword.toLowerCase()}</span>`)
  }
  return text
}
body{
  margin:0;
  overflow: hidden;
}
.code-editor-container{
  overflow: hidden;
  width: 100vw;
  height: 100vh;
  position:relative;
}

.code-editor{
  box-sizing: border-box;
  width:100%;
  height: 100%;
  width: 100%;
  position: absolute;
  top: 0;
  padding: 10px;
  margin: 0;
  font-family: 'Roboto', sans-serif;
  font-size: 1.5rem;
  padding: 30px;
  border: none;
  outline: none;
  line-break: anywhere;
  white-space: pre-wrap;
  color:white;
  
}
.code-editor-background{
  resize: none;
  background-color:rgb(44, 44, 44);
  caret-color:white;
  color: transparent;
}

.code-editor-background::selection {
  color: transparent;
  background: #239123;
}
.code-editor-foreground{
  pointer-events: none; left:1px;top:1px
}
<div class="code-editor-container">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
      <textarea class="code-editor code-editor-background" id="in"></textarea>
      <div class="code-editor code-editor-foreground" id="out"></div>
    </div>
Ecumenicist answered 7/8, 2022 at 13:5 Comment(0)
L
1

This isn't an answer to this question, but it answers the title question, which is found when you a google search about highlighting a word in a textarea.

A colored selection can be made in a textarea element using the built-in API setSelectionRange function and the ::selection css selector.

Note, it only supports one text selection at a time and only until the textarea gets the focus.

  const input = document
        .getElementById( 'text-box' );
  
  var i, l;

  input.focus();
  input.value = input.value.trim();
  
  i = input.value .indexOf( 'programming' );
  l = ( 'programming' ).length;
  
  // https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange
  
  input
  .setSelectionRange( i, l + i );
::-moz-selection {
  background-color: yellow;
  color: red;
}
::selection {
  background-color: yellow;
  color: red;
}
<textarea id="text-box" size="40">
  I like programming with JavaScript!
</textarea>
Lorrin answered 7/1, 2021 at 6:51 Comment(0)
P
1

I found another unique solution that is pretty easy compared to the others. Basically the idea is to put some highlighted code behind a textarea and make the textarea invisible except for the caret.

I used https://github.com/jgnewman/custom-syntax-highlighter to define my own language. You can use whatever you want.

The idea is described here but I added a minimal example: https://css-tricks.com/creating-an-editable-textarea-that-supports-syntax-highlighted-code/ You should still read there to fix a scrolling issue

Try to enter the following below: callFunction('with string')

//on reload the textarea will still contain text. We have to put that into the code element and format the text
function init() {
    update(document.getElementById('editing').value);
}
window.onload = init;

function update(text) {

  //put textarea content into code tags
  let result_element = document.querySelector('#highlighting-content');
  result_element.innerText = text;

  // Syntax Highlight define your own Regex. Names will be css classes to stile
  highlight({
    patterns: [{
        name: 'string',
        match: /^(\'[^\'\n]*\')/,
      },
      {
        name: 'fn-call',
        match: [/^([A-z_]+)\(/, '', '('],
      },
    ],
  });
}
html,
body {
    margin: 0px;
    padding: 0px;
    height: 100%;
}

.container {
    display: grid;
    height: 100%;
    margin: 0px;
    padding: 0px;
}

pre,
code {
    white-space: pre-line;
    /*Remove indetend on first line*/
}

#editing,
#highlighting {
    /*position above each other*/
    grid-column: 1;
    grid-row: 1;
    margin: 0px;
    padding: 0px;
    /*make sure to apply text styling to both*/
    font-size: 15pt;
    font-family: monospace;
    line-height: 20pt;
}

#editing {
    color: transparent;
    background: transparent;
    caret-color: white;
    color: transparent;
}


/*syntax highlighting class names defined in js code*/

.fn-call {
    color: red;
}

.string {
    color: blue;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <script src="https://unpkg.com/custom-syntax-highlighter@latest/bin/index.js"></script>
  <script>
    var highlight = window.csHighlight; //need to initialize the library
  </script>

</head>

<body>

  <!--Container is display grid to position textarea and code above each other-->
  <div class="container">
    <!--the code element that will be highlighted-->
    <pre id="highlighting" aria-hidden="true">
      <code id="highlighting-content">
      </code>
    </pre>

    <!--make sure the textarea is in front and can be interacted with. disable spell check-->
    <textarea id="editing" spellcheck="false" oninput="update(this.value);"></textarea>
  </div>

</body>

</html>

Have a nice day

Provender answered 21/12, 2021 at 22:39 Comment(0)
P
0

// SQL keywords
var keywords = ["SELECT","FROM","WHERE","LIKE","BETWEEN","NOT LIKE","FALSE","NULL","FROM","TRUE","NOT IN"];
// Keyup event
$("#editor").on("keyup", function(e){
  // Space key pressed
  if (e.keyCode == 32){
    var newHTML = "";
    // Loop through words
    $(this).text().replace(/[\s]+/g, " ").trim().split(" ").forEach(function(val){
      // If word is statement
      if (keywords.indexOf(val.trim().toUpperCase()) > -1)
        newHTML += "<span class='statement'>" + val + "&nbsp;</span>";
      else
        newHTML += "<span class='other'>" + val + "&nbsp;</span>"; 
    });
    $(this).html(newHTML);

    // Set cursor postion to end of text
    var child = $(this).children();
    var range = document.createRange();
    var sel = window.getSelection();
    range.setStart(child[child.length-1], 1);
    range.collapse(true);
    sel.removeAllRanges();
    sel.addRange(range);
    this.focus();
  }
});
#editor {
    width: 400px;
    height: 100px;
    padding: 10px;
    background-color: #444;
    color: white;
    font-size: 14px;
    font-family: monospace;
}
.statement {
    color: orange;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="editor" contenteditable="true"></div>
Prissy answered 14/10, 2021 at 0:26 Comment(0)
P
-1

you can use this code

<code contenteditable="true">
  <span style="color: orange">SELECT</span> *
  <span style="color: orange">FROM</span>
   TABLE
  <span style="color: orange">WHERE</span>
  id = 2
</code>
Pohl answered 1/10, 2020 at 14:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.