Vb Dev
Vb Dev

Reputation: 247

Angular 2 contenteditable div, move caret to end position

I have following Angular 2 code:

write.component.ts:

 import { CommentHeaderComponent }                            from './comment.header.component';
 import { StyleService }                                      from './../../services/services/style.service';
 import { Parameters }                                        from './../../services/services/parameters';
 import { Component, Input, ViewChild,ElementRef,HostBinding} from "@angular/core";
 import { ContenteditableModel }                              from './../directives/contenteditable.directive';

@Component({
            selector:'write',
            templateUrl:'app/templates/write.component.html',
            styleUrls:['app/templates/css/write.component.css']
           })

export class WriteComponent 
{
  @HostBinding('class.hbox')
  parameters:Parameters;
  private writeText:string="";
  private rows:number=1;
  private maxRows:number=4;
  private comment:boolean=false;
  private lineHeight:string="1.7em";

  @ViewChild('writeBox') writeBox:ElementRef;

  constructor(private stService:StyleService){}

  ngOnInit()
  {
      this.writeBox.nativeElement.innerText="";
      this.stService.setProperty(this.writeBox,[{rows:this.rows}]);
      this.stService.setStyle(this.writeBox,[{lineHeight:this.lineHeight}]);
      if (this.parameters.Iskey("comment"))
      {
        this.comment=true;
      }
  }

  write(data:any)
  {
   this.parameters.cfunc({event:"comment",message : this.writeText});
   this.writeText="";
  }      

  getWriteText():String
  {
    return this.writeText;
  }
}

write.component.html:

<div contenteditable="true" #writeBox  [ceModel]="writeText" (ceChange)="writeText=$event" [innerHTML]="writeText"> 
</div>
<ng-template [ngIf]="this.comment">
  <button type="button" (click)="files($event)"   class="btn fa fa-paperclip"> </button>
</ng-template>
<button type="button" (click)="write($event)"   class="btn fa fa-chevron-right"> </button>

contenteditable.directive.ts:

import { ValidatorService } from './../../services/service/validator.service'; import { Directive, HostListener, Input, ElementRef, OnInit, SimpleChanges, EventEmitter, Output } from '@angular/core';

@Directive({selector: '[ceModel]'})

export class ContenteditableModel 
{
    @Input('ceModel') ceModel: any="";
    @Output('ceChange') ceChange = new EventEmitter();


    constructor(private elRef: ElementRef,private v:ValidatorService) {}

    @HostListener('keyup', ['$event'])
    onChange($event: any) 
    {

     if ($event.keyCode==32)
     {
        console.log(this.ceModel);
        this.ceModel="<a>"+this.ceModel+"<\a>";
        this.ceChange.emit(this.ceModel);
        return;
     }


     this.ceModel=this.elRef.nativeElement.innerText;
     this.ceChange.emit(this.ceModel);
    }

    @HostListener('paste', ['$event'])
    onPaste($event: any) 
    {
     console.log($event);
    }
 }

I want to update dynamically a contenteditable div, see write.component.html, that is linked to a model that is managed by a directive,contenteditable.directive.ts. The class variable writeText is sent to the directive in order to check whether a user has written a url, if this is the case, the url content should be transformed to: url. This is rendered in the div as html:

enter image description here

enter image description here

The problem is, that whenever I type some text, the caret/cursor jumps always to the start position:

enter image description here

Is it possible to manually move the cursor to end of the div content? The content may be text and html. I have tried the solutions proposed here with no luck:

Set the caret position always to end in contenteditable div

Thank you for your help.

Upvotes: 1

Views: 4093

Answers (2)

Hiep Tran
Hiep Tran

Reputation: 4093

enter image description here in my problem, I trigger focus when enter (it auto go to new line) and refocus (to the last character of last todo) if i delete

....
 // somewhere you trigger that focus event:
 (div:HTMLElement) => {
   // div.focus();
   if (div.lastChild) this.setSelectionRange(div)
   else div.focus();
   this.changeDetector.detectChanges();
 }
....

setSelectionRange(el:HTMLElement) {
    let range = document.createRange();
    let pos = el.lastChild.textContent.length;
    let sel = window.getSelection();
    console.log('el last child',el.lastChild.textContent.length,typeof(el.lastChild.textContent.length));
    range.setStart(el.lastChild, pos);
    range.collapse(true);
    sel.removeAllRanges();
    sel.addRange(range);
  }

notice at "el.lastChild", your contenteditable using (textContent or innerHTML?)

Upvotes: 2

Alessandro Cappello
Alessandro Cappello

Reputation: 528

I hope you're still interested in getting an answer to this. The easiest approach I can imagine is to save a reference to the <a> node and use it to place the caret.

Inside ContenteditableModel you simply can create the following:

private moveCaret(): void {

    let range = document.createRange(),
        pos = this.elRef.lastChild.innerText.length - 1,
        sel = window.getSelection();

    range.setStart(this.elRef, pos);
    range.collapse(true);
    sel.removeAllRanges();
    sel.addRange(range);
}

This allows you to use your elRef to place the caret at the last char of its last child, the <a> element.

Upvotes: 1

Related Questions