Search code examples
javascripthtmlinputtokensyntax-highlighting

How can I syntax highlight a single text input field?


How can I best syntax highlight a single text area, with extremely simple highlighting rules?

Javascript-based code editors are huge and bloated - I don't want multi-line editing, line numbers, or anything like that. Just something simple that parses inputted text and allows formatting on it.

For example, if the user is creating a message template and has tokens available called firstName and lastName, and there is an HTML single-line input field available, if they type:

Hello, {{firstName}} {{lastName}}!

What would be the best method to apply styling (colors, borders, etc) to the well-defined tokens, while still allowing the user to edit the entire string of text?


Solution

  • Well, I have a function that I created that highlight words when it is inside [...], I made an adjustment for you and then it gets what is inside {{...}}.

    But the problem is that, you can't set HTML in an input field, so you can't highlight words in a simple way inside inputs or textareas, you need to add the highlited text to another kind of element.

    A possible solution to that, would be to have a div with contentEditable=true, so maybe it will be easier to type and highlight in the same input.

    Look the below code (where I made based on a simple input, not contentEditable div) if it is what you are looking for.

    function setHighlight(input){  
      let newText = Highlight(input.value);
      document.getElementById("result").innerHTML = newText;
    }
    
    function Highlight(text){
      var htmlText = text;
      var attrs = htmlText.match(/\{{(.*?)\}}/g);
    
      if (attrs != null) {
          var stringAttrs = attrs.join(" ")
          attrs = stringAttrs.replace(/}}/g, '').replace(/\{{/g, '').split(" ");
          for (var i = 0; i < attrs.length; i++) {
              var attr = attrs[i];
              if (attr.length > 0) {
                  attr = "{{" + attr + "}}";
                  if (htmlText.indexOf(attr) > -1) {                  
                      htmlText = htmlText.replace(attr, "<span class='highlight'>" + attr + "</span>");
                  }
              }
          }
      }
      return htmlText;
    }
    input{
      width: 320px;
      height: 40px;
    }
    
    .highlight{
      font-weight: bold;
      color: #14e;
    }
    <input id="txt" oninput="setHighlight(this)" value="type a {{token}} here">
    
    <div id="result"></div>

    And here, I did with a contentEditable, it seems more what are you looking for:

    function setHighlight(inputSpan){  
      let newText = Highlight(inputSpan.textContent);
      inputSpan.innerHTML = newText;
      setCaretToEnd(inputSpan);
    }
    
    function Highlight(text){
      var htmlText = text;
      var attrs = htmlText.match(/\{{(.*?)\}}/g);
    
      if (attrs != null) {
          var stringAttrs = attrs.join(" ")
          attrs = stringAttrs.replace(/}}/g, '').replace(/\{{/g, '').split(" ");
          for (var i = 0; i < attrs.length; i++) {
              var attr = attrs[i];
              if (attr.length > 0) {
                  attr = "{{" + attr + "}}";
                  if (htmlText.indexOf(attr) > -1) {                  
                      htmlText = htmlText.replace(attr, "<span class='highlight'>" + attr + "</span>");
                  }
              }
          }
      }
      return htmlText;
    }
    
    function setCaretToEnd(elem){
      let range = document.createRange();
      range.selectNodeContents(elem);
      range.collapse(false);
      let selection = window.getSelection();
      selection.removeAllRanges();
      selection.addRange(range);
    }
    #txt{
      width: 320px;
      height: 30px;
      padding: 4px;
      border: 1px solid #777;
      display: block;
      border-radius: 4px;
      color: #222;
    }
    
    .highlight{
      font-weight: bold;
      color: #14e;
    }
    <span id="txt" contentEditable="true" oninput="setHighlight(this)">type a {{token}} here</span>