Skip to content Skip to sidebar Skip to footer

How To Detect And Trim Leading/trailing Whitespace In Quill (rich Text Editor)?

How can you detect and delete leading/trailing whitespace in Quill, which is a rich text editor? For instance, the sample HTML below represents Quill output for the text '\nHi'. We

Solution 1:

Quill uses the Delta class to describe the rich text format and we can use Quill getContents method to get all content entries which will be in the following format:

Delta {
  ops: [
    insert: "↵↵↵Hello↵world!↵↵"
  ]
}

We have to create a logic using a loop through all these entries, detect and eliminate leading and ending line feeds.

We use two arrays, one array that we'll store the new delta entries and one for storing the pending ending line feeds that we may or may not append to the new delta entries array. We'll also have a flag indicating whether we've fixed the leading line feeds.

This solution uses this method "handling blur event" to detect the blur event.

Quill blur code and snippet (run to see it in action):

let quills = [];

[...document.getElementsByClassName('quill-editor')].forEach((el, idx) => {
  const quill = newQuill(el, {
    modules: {
      toolbar: [
        [{ header: [1, 2, false] }],
        ['bold', 'italic', 'underline'],
        ['image', 'code-block']
      ]
    },
    placeholder: 'Compose an epic...',
    theme: 'snow'// or 'bubble'
  });

  quill.on('selection-change', function(range, oldRange, source) {
    if (range === null && oldRange !== null) {
      const delta = quill.getContents();

      let leadingFixed = false;
      let newDelta = [];
      let tempDelta = [];

      if(delta.ops.length === 1) {
        // If there is only one entry, check if it's a string and trim leading and ending LFlet { insert, attributes } = delta.ops[0];
        if(typeof(insert) === 'string') {
          insert = insert.replace(/^\n+|\n+$/g, '');
        }
        newDelta = [{ insert, attributes }];
      } else {
        // Else go through all the insert entries
        delta.ops.forEach(({ insert, attributes }, idx) => {
          // Create a boolean to indicate if we're at the last entryconst isLast = idx === delta.ops.length - 1;

          // If the entry is a string (not image/asset)if(typeof(insert) === 'string') {
            // If we haven't fixed the leadingif(!leadingFixed) {
              // If the entry begins with LFsif(/^\n+/.test(insert)) {
                // Create a string witrh clean leading LFslet cleanText = insert.replace(/^\n+/, '');

                // If there is text after cleaning the LFsif(cleanText.length > 0) {
                  // Add the text to the newDelta
                  newDelta.push({
                    insert: cleanText,
                    attributes
                  });
                  // Set leading flag to indicate we've fixed the leading
                  leadingFixed = true;
                }
              // Else if the entry does not start with LFs
              } else {
                // If the entry does not begin with LFs// Add any pending entries that may exists in tempDelta to the newDelta
                newDelta = newDelta.concat(tempDelta);
                // Add the existing entry
                newDelta.push({
                  insert,
                  attributes
                });
                // Clean the any pending entries
                tempDelta = [];
                // And set the leading flag to indicate we've fixed the leading
                leadingFixed = true;
              }
            // Else if we have fixed the leading
            } else {
              // If there an entry with ending LFsif(/\n+$/.test(insert)) {
                // Create a string witrh clean ending LFslet cleanText = insert.replace(/\n+$/, '');

                // If this is the last entryif(isLast) {
                  // If there is text after cleaning the LFsif(cleanText.length > 0) {
                    // Add any pending entries that may exists in tempDelta to the newDelta
                    newDelta = newDelta.concat(tempDelta);
                    // Add the cleaned entry
                    newDelta.push({
                      insert: cleanText,
                      attributes
                    });
                  }
                // Else if this is not the last entry
                } else {
                  // If there is text after cleaning the LFsif(cleanText.length > 0) {
                    // Add any pending entries that may exists in tempDelta to the newDelta
                    newDelta = newDelta.concat(tempDelta);
                    // Add the existing entry
                    newDelta.push({
                      insert,
                      attributes
                    });
                    // Clean the any pending entries
                    tempDelta = [];
                  // Else if there is no text after cleaning the LFs
                  } else {
                    // Add the entry to the temp deltas so to use them later if its needed
                    tempDelta.push({ insert, attributes });
                  }
                }
              // Else if the entry does not end with LFs
              } else {
                // Add any pending entries that may exists in tempDelta to the newDelta
                newDelta = newDelta.concat(tempDelta);
                // Add the existing entry
                newDelta.push({
                  insert,
                  attributes
                });
                // Clean the any pending entries
                tempDelta = [];
              }
            }
          // If the entry is not a string
          } else {
            // Then all leading text/line feeds have been cleared if there were any// so, it's safe to set the leading flag
            leadingFixed = true;
            // Add any pending entries that may exists in tempDelta to the newDelta
            newDelta = newDelta.concat(tempDelta);
            // Add the existing entry
            newDelta.push({
              insert,
              attributes
            })
            // Clean the any pending entries
            tempDelta = [];
          }
        });
      }

      quill.setContents(newDelta);
    } /*else if (range !== null && oldRange === null) {
      console.log('focus');
    }*/
  });

  quills.push(quill);
});
.editors {
  display: flex;
}

.quill-editor-container {
  flex: 1;
}
.quill-editor {
  height: 100px!important;
}
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/quill/1.3.7/quill.core.js"integrity="sha256-jvauzib5XGeoiDyHV6mlZnpuKsEAcjhruilbo0e+L6k="crossorigin="anonymous"></script><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/quill/1.3.7/quill.js"integrity="sha256-CN8TogJAzCcMj0uYishm9mmawPUFKJeJh//zR/CfCO8="crossorigin="anonymous"></script><linkhref="https://cdnjs.cloudflare.com/ajax/libs/quill/1.3.7/quill.core.css"integrity="sha256-2kIq+5smyR4blGwdXXCCVrPLENwavLyrG8+kLPfDPJk="crossorigin="anonymous" /><linkhref="https://cdnjs.cloudflare.com/ajax/libs/quill/1.3.7/quill.bubble.css"integrity="sha256-2hxHujXw890GumwDHPWrwJCtdZZdrJanlGsrOTSfXnc="crossorigin="anonymous" /><linkhref="https://cdnjs.cloudflare.com/ajax/libs/quill/1.3.7/quill.snow.css"integrity="sha256-jyIuRMWD+rz7LdpWfybO8U6DA65JCVkjgrt31FFsnAE="crossorigin="anonymous" /><divclass="editors"><divclass="quill-editor-container"><divclass="quill-editor"></div></div><divclass="quill-editor-container"><divclass="quill-editor"></div></div></div>

I've tested it with image assets and formatting and it seems to work pretty well. Of course the code can be further optimized and maybe simplified.

You can also check this Stackblitz project if you wanna fork it and make some tests.

Post a Comment for "How To Detect And Trim Leading/trailing Whitespace In Quill (rich Text Editor)?"