I am new to javascript.I am trying to make an simple toggle up div With user selection direction ill place the toggle div.After some googling I found one working fiddle But not as expected See the below screenshot to see the difference
When I select Some text on the front of the paragraph It works fine Like this
But when I selected some text from bottom paragraph It is not Working as expected
JsFiddle
Actually I am working in React version Fiddle is in Jquery
This is my code
import React from 'react'
import {render} from 'react-dom';
export default class App extends React.Component{
constructor(props){
super(props);
this.state = {
display:'none' ,
top:'',
bottom:'',
left:'',
right:'',
diplayForDown:'none'
};
this.handleOnMouseDown = this.handleOnMouseDown.bind(this)
this.onMounseUp = this.onMounseUp.bind(this)
this.onMouseDwn = this.onMouseDwn.bind(this)
this.triggerAlltime = this.triggerAlltime.bind(this)
}
handleOnMouseDown(){
let sel = window.getSelection && window.getSelection();
let r = sel.getRangeAt(0).getBoundingClientRect();
let relative=document.body.parentNode.getBoundingClientRect();
console.log('Relative ',relative);
if(!sel.isCollapsed){
console.log(sel,r);
let display = 'block';
let top = (r.bottom - relative.top - 80)+'px';
let bottom = r.bottom+'px';
let left =( r.left)+'px';
let right = (r.right)+'px';
console.log('This is Height',r.bottom-r.top);
let selectionHeight = r.bottom - r.top;
if(selectionHeight => 22.22){
this.setState({
display,
top:top,
bottom,
left,
right
})
}else{
this.setState({
display,
top,
bottom,
left,
right
})
}
}else{
this.setState({
display:'none'
})
}
console.log('Slected')
}
onMounseUp(e){
e.preventDefault()
let sel = window.getSelection && window.getSelection();
if(!sel.isCollapsed){
console.log('Moved Up')
}
}
onMouseDwn(e){
let sel = window.getSelection && window.getSelection();
if(!sel.isCollapsed){
console.log('Moved Down')
}
}
getSelectionHtml() {
let html = "";
if (typeof window.getSelection != "undefined") {
let sel = window.getSelection();
if (sel.rangeCount) {
let container = document.createElement("div");
for (let i = 0, len = sel.rangeCount; i < len; ++i) {
container.appendChild(sel.getRangeAt(i).cloneContents());
}
html = container.innerHTML;
}
} else if (typeof document.selection != "undefined") {
if (document.selection.type == "Text") {
html = document.selection.createRange().htmlText;
}
}
console.log('html',html)
return html;
}
lastCharRTL(txt) {
return /[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]$/.test(txt);
}
triggerAlltime(e){
// console.log('Some Thinms')
if(!window.getSelection().isCollapsed){
//find the Direction Of Slelection
let sel = window.getSelection();
console.log(sel)
let range = document.createRange();
range.setStart(sel.anchorNode, sel.anchorOffset);
range.setEnd(sel.focusNode, sel.focusOffset);
let backwards = range.collapsed;
range.detach();
// get selection rects
let rects = sel.getRangeAt(0).getClientRects();
let n = rects.length - 1;
let display = 'block';
console.log(this.lastCharRTL(this.getSelectionHtml()))
if (this.lastCharRTL(this.getSelectionHtml()))
this.setState({
display:'none',
diplayForDown:'none',
top: rects[n].top + 10,
left: rects[n].left - 10
})
else if (backwards)
this.setState({
display,
diplayForDown:'none',
top: rects[0].top + -68,
left: rects[0].left
})
else
this.setState({
display:'none',
diplayForDown:'block',
top: rects[n].top + 40,
left: rects[n].right+-160
})
}else{
this.setState({
display:'none',
diplayForDown:'none',
})
}
}
render(){
return(
<div className="container">
<div className="CenterCon">
<div contentEditable="true" onMouseUp={this.triggerAlltime} onMouseDown={this.triggerAlltime} onKeyUp={this.triggerAlltime} onKeyDown={this.triggerAlltime} className="Edithis" >
<p>Test</p>
</div>
</div>
<div style={{top: this.state.top, bottom: this.state.bottom, left:this.state.left, right:this.state.right, display: this.state.display}} className="Toggle">
<ul>
<li>B</li>
<li>U</li>
<li>H</li>
<li>"</li>
</ul>
<div className="triangle">
</div>
</div>
{/*DownWard Toggle*/}
<div style={{top: this.state.top, bottom: this.state.bottom, left: this.state.left, right: this.state.right, display: this.state.diplayForDown}} className="ToggleForDownWardSelection">
<div className="triangle-bottom" />
<ul>
<li>B</li>
<li>U</li>
<li>H</li>
<li>li</li>
</ul>
</div>
</div>
)
}
}
render(<App/>,document.getElementById('app'));
At Last I find the answer,I fixed it apply simple style to the contentEditable div And It it is Working like a Charm.I don't know why it is working But i doubt window API
<div contentEditable="true" style={{height:'100vh',overflow:'auto'}} onMouseUp={this.triggerAlltime} onMouseDown={this.triggerAlltime} onKeyUp={this.triggerAlltime} onKeyDown={this.triggerAlltime} className="Edithis" >
<p>Test</p>
</div>
The issue is that the #pointer
is set to absolute position, but when the content is long and scrollable, the top distance is calculated from top of window rather than top of the content.
Adding height: 100vh
would solve this problem, but you may see two scroll bars in some cases.
Another way is to add window.pageYOffset
when calculating the top
of pointer div.
if (backwards) {
$('#pointer').css({top: rects[0].top + window.pageYOffset + 10, left: rects[0].left - 10}).show();
} else {
$('#pointer').css({top: rects[n].top + window.pageYOffset + 10, left: rects[n].right}).show();
}
Here is DEMO
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With