TExtarea iMproved Part 2: better lists, auto-resize, and more!

Welcome to v2 of the TExtarea iMproved! We're gonna add a few more features to our previous textarea improver to make it nicer to use.

Here's what it looks like:

This one won't actually make much sense without reading the previous part of this series, so go read that first!

Anyways, we got a lot more features to work out, so let's get straight in!

Automatic list item insertion

Wouldn't it be nice if you could just hit enter from a list item to get a new item added for you automatically? Something like this:



cropped-list-auto



That's a great feature we could use, and it's really easy to implement!

So, first we should add an event listener for 'Enter':

editing.addEventListener('keydown', function (event) {
    // Other older stuff...
    if (event.key === 'Enter') {
        // Code...
    }
}

Next thing to do is figure out whether the line being edited actually contains a bullet. We can abstract that to a new function, and first thing to do is get the part of the line being edited:

function looksLikeBullet(text, caretPos) {
    let line = text.substring(0, caretPos).split(/\r?\n|\r/).pop();
}

Then we can use this kind-of-confusing regex to check if there's a bullet:

let bulletRegex = /^([ \t]*[\*\-\+]\s*).*/gim;
if (bulletRegex.test(line)) {
    return {
        bullet: line.replace(bulletRegex, '$1')
    };
}
return false;

The regex is not so confusing, here's an explanation:

/^([ \t]*[\*\-\+]\s*).*/gim

/        // Start of regex
^        // Start of line
(        // Capturing group
[ \t]*   // Any number of spaces or tabs
[\*\-\+] // Any of the bullet symbols +, -, or *
\s*      // The whitespace after the bullet. We capture this so that we can use it later 
)        // End of capturing group
.*       // Anything, ie. the rest of the bullet
/gim     // End of regex, and tags global, case insensitive, and multiline

So, next thing to do is use the function in our event listener:

let bullet = looksLikeBullet(editing.value, editing.selectionStart);
if (bullet) {
    // Code...
}

And then insert the bullet:

// Prevent the newline from being added:
event.preventDefault();
// Store the text after the cursor, so it can be added to the next line:
let addition = editing.value.substring(editing.selectionStart);
// Remove the text after the cursor:
editing.value = editing.value.substring(0, editing.selectionStart);
// Insert the bullet in the textarea
editing.value += ('\n' + bullet.bullet + addition);

And it's done! You can now easily make lists.

Fixing it for numbered lists

Of course, this doesn't work for numbered lists, so we have to fix that. We can easily just make an else block in the looksLikeBullet function, and check for numbers.

...
let numberedListRegex = /^[ \t]*(\d+\.\s*).*/gim;
if (...) {
...
} else if (numberedListRegex.test(line)) {
    return {
        bullet: line
                .replace(numberedListRegex, "$1")
    }
}
return false;

But, the problem is that numbers don't get auto incremented. So we can fix that using yet another regex to increment numbers:

bullet: line
        .replace(numberedListRegex, "$1")
        .replace((/\d+/, (number) => +number + 1)



list-auto-number

That works well

Auto resize

It's so not nice to have to resize a textarea manually – I mean we have to use the mouse! We should fix that.

This one is quite simple, just create a resize function:

function resize() {
    editing.style.height = '24px';
    editing.style.height = this.scrollHeight + 12 + 'px';
}

And then listen to the input event:

editing.addEventListener('input', resize)

But then, we also need to resize when we insert text programmatically, so we can call resize in the keydown listener also. If we do that, we get the perfect auto-resize

resize

That's really helpful

There are better ways of doing this out there, but we'll keep this for now

That's it, we got an even better textarea which we can use!
There's a huge (Edit: 100%) probability of the third part of this series coming out someday, so stay tuned!

19