Search code examples
javascripthtmlangulartextareakeyboard-shortcuts

Javascript Textarea prevent Alt gr + key combinations on KeyDown/KeyUp events itself


In my text area im using onKeydown event to listen to characters typed by user. I need to prevent Alt gr + key combination values (ē, r̥, ṭ,...) to avoid accented keys in my text area.

if (event.code === 'AltRight')  {
      console.log('Prevent Accent letters');
      event.preventDefault();
    }

(or)

if (event.code === 17 || event.code === 18)  {
      console.log('Prevent Accent letters');
      event.preventDefault();
    }

Nothing worked. They are entering these if condition but still the accent text is getting printed in my textarea. How can this be prevented


Solution

  • We need to create a custom directive which works with the regex /[\\x00-\\x7F]*/i to negate all the alt code characters, and only filters out the valid ones, we can use match to do this!

    Alt Codes wiki

    Article used for deriving this answer

    Regexr to check the regex

    SIMPLE VERSION:

    import { Component } from '@angular/core';
    import { bootstrapApplication } from '@angular/platform-browser';
    import 'zone.js';
    import { PermitDirective } from './app/input.directive';
    
    @Component({
      selector: 'app-root',
      imports: [PermitDirective],
      standalone: true,
      template: `
        <textarea (input)="onInputChange($event)"></textarea>
      `,
    })
    export class App {
      name = 'Angular';
    
      onInputChange(event: any) {
        event.target.value = event.target.value.match(/[\\x00-\\x7F]*/i).toString();
      }
    }
    
    bootstrapApplication(App);
    

    Stackblitz Demo


    COMPLEX VERSION (works for paste also):

    directive

    import { Directive, HostListener, Input } from '@angular/core';
    
    @Directive({
      selector: '[appPermit]',
      standalone: true,
    })
    export class PermitDirective {
      @Input('regex') pattern: RegExp = /[\\x00-\\x7F]*/i;
    
      constructor() {}
    
      @HostListener('input', ['$event']) onInput(e: any) {
        e.target.value = this.isValidText(e, '');
      }
    
      @HostListener('paste', ['$event'])
      onPaste(event: any) {
        return this.isValidText(event, event.clipboardData.getData('text/plain'));
      }
    
      isValidText(e: any, text: any) {
        debugger;
        const [selectionStart, selectionEnd] = this.getSelectionRange(e.target);
        // for elements not having value accessor(innerText)
        const existingValue = e.target.value ?? e.target.innerText;
        console.log(
          e.target.value,
          'typed',
          existingValue.substring(0, selectionStart) +
            text +
            existingValue.substring(selectionEnd)
        );
        return (
          existingValue.substring(0, selectionStart) +
          text +
          existingValue.substring(selectionEnd)
        )
          .match(this.pattern)
          .toString();
      }
    
      getSelectionRange(el: HTMLElement) {
        if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
          // These properties aren't present for any HTMLElement
          return [el.selectionStart, el.selectionEnd];
        }
        // currently getSelection() doesn't work on the content of <textarea> and // <input> elements in Firefox, Edge (Legacy)
        const { startOffset, endOffset } = <any>getSelection()?.getRangeAt(0);
        return [startOffset, endOffset];
      }
    }
    

    main.ts

    import { Component } from '@angular/core';
    import { bootstrapApplication } from '@angular/platform-browser';
    import 'zone.js';
    import { PermitDirective } from './app/input.directive';
    
    @Component({
      selector: 'app-root',
      imports: [PermitDirective],
      standalone: true,
      template: `
        <textarea appPermit></textarea>
      `,
    })
    export class App {
      name = 'Angular';
    }
    
    bootstrapApplication(App);
    

    Stackblitz Demo