I want to get something like this, in textarea. I cant change textarea to contentaditable div. How do I get the position of the selected text? I need this to show this pop-up from above
2 Answers
You should be able to easily convert this into angular code. This is the basics of what you need to do, its not thoroughly tested but it bare minimum works.
const textarea = document.getElementById('text')
const result = document.getElementById('selected')
const tooltip = document.getElementById('tooltip')
const cols = document.getElementById('text').cols;
const width = document.getElementById('text').clientWidth;
const height = window.getComputedStyle(textarea).lineHeight;
textarea.onclick = function getSelection() {
const pos = {
top: textarea.offsetTop,
left: textarea.offsetLeft,
};
result.textContent = `${textarea.selectionStart}, ${textarea.selectionEnd}`;
let selection
if (textarea.selectionStart) {
selection = textarea.selectionStart;
} else if (document.selection) {
textarea.focus();
const r = document.selection.createRange();
if (r == null) {
selection = 0;
}
let re = textarea.createTextRange();
let rc = re.duplicate();
re.moveToBookmark(r.getBookmark());
rc.setEndPoint('EndToStart', re);
selection = rc.text.length;
} else {
selection = 0
}
const row = Math.floor((selection - 1) / cols);
const col = (selection - (row * cols));
const x = Math.floor((col * (width / cols)));
const y = (parseInt(height) * row);
tooltip.innerHTML = "<b>row: " + row + "<br>columns" + col + "<br>width: " + width + "</b>";
tooltip.style.top = `${pos.top+y}px`;
tooltip.style.left = `${pos.left+x+10}px`;
}
textarea {
height: 80px;
line-height: 12px;
overflow-y: scroll;
display: block;
}
#tooltip {
position: absolute;
background:red;
color: white;
}
<textarea id="text">Lopsum</textarea>
<span id="tooltip"></span>
<span id="selected"></span>

- 3,506
- 1
- 21
- 41
-
How can knowing the index help me in displaying a popup? I would like to know the height and width at which the selection is located – Dec 22 '21 at 08:23
-
width: 151 every time – Dec 22 '21 at 09:22
-
Thats the width of the text area, its static. Please check code first. – Njuguna Mureithi Dec 22 '21 at 09:24
-
but i need postition of selected text in px – Dec 22 '21 at 09:33
-
The x, y positioning should be equal to `tooltip.style.left, tooltip.style.top` – Njuguna Mureithi Dec 22 '21 at 09:37
The only way I imagine is create a duplicate of the text area (very similar to this SO, (the Owen Kelvin's response) about highligth words in a text area
As we only need the position, we can split the "texarea" and the "background". Futhermore, using the Yurzui response in this another SO we can control when resize the textarea
We can imagine an .html like
<div class="container">
<div
#backdrop
class="backdrop"
[style.width.px]="textWidth"
[style.height.px]="textHeight"
>
<div
class="highlights"
[innerHTML]="sanitizer.bypassSecurityTrustHtml(highlightedText)"
></div>
</div>
</div>
<textarea
#textarea
spellcheck="false"
(resize)="rect = null;"
(select)="applyHighlights(textarea.value)"
(mousedown)="mouseDown()"
(blur)="rect = null"
[ngModel]="textValue"
(ngModelChange)="textValue = $event; applyHighlights(textarea.value)"
(scroll)="handleScroll(); applyHighlights(textarea.value)"
></textarea>
<div
class="tooltip"
*ngIf="rect"
[style.top.px]="rect.y"
[style.left.px]="rect.x"
></div>
See that the "text" is hidden because we has a div container like
.container {
overflow:hidden;
width:0;
height:0;
}
And we make that the dimensions of "text" are condition by the two variables "textWidth" and "textHeight"
The code is
ngOnInit() {
this.resize();
}
resize() {
const event = {
width: this.$textarea.nativeElement.getBoundingClientRect().width,
height: this.$textarea.nativeElement.getBoundingClientRect().height,
};
this.textWidth = event.width;
this.textHeight = event.height;
}
mouseDown() {
setTimeout(() => {
const start = this.$textarea.nativeElement.selectionStart;
const end = this.$textarea.nativeElement.selectionEnd;
if (start == end) this.rect = null;
});
}
applyHighlights(text: string) {
if (text) {
let start = this.$textarea.nativeElement.selectionStart;
let end = this.$textarea.nativeElement.selectionEnd;
if (start == end) this.highlightedText = text;
else {
const selected = text.substr(start, end - start);
this.toolTipText=this.getTooltipText(selected)
this.highlightedText =
text.substr(0, start) +
"<span id='mark'>" +
selected +
'</span>' +
text.substr(end);
this.resize();
setTimeout(() => {
const recArea = this.$textarea.nativeElement.getBoundingClientRect();
const recText = this.$backdrop.nativeElement.getBoundingClientRect();
const rect = document.getElementById('mark').getBoundingClientRect();
rect.y += window.scrollY;
rect.x+=rect.width/2
this.rect = rect.y - window.scrollY < recArea.y ? null : rect;
});
}
}
}
handleScroll() {
var scrollTop = this.$textarea.nativeElement.scrollTop;
this.$backdrop.nativeElement.scrollTop = scrollTop;
var scrollLeft = this.$textarea.nativeElement.scrollLeft;
this.$backdrop.nativeElement.scrollLeft = scrollLeft;
}
And in the stackblitz I put in a custom form component (the reason is that Owen make this work for me

- 50,109
- 4
- 29
- 67