I am trying to create a Firefox extension, where I want a popup to appear next to some text which is double clicked. As I am placing this code as my extension's content script which will be loaded on ANY website, I have tried to not use jQuery for this task.
I have seen and implemented the answers given in the links:
How to position popover over a highlighted portion of text?
How to position a popup div based on the position of where the cursor clicks
Using mouse/cursor coordinates to position popup
Unfortunately most of these answers were in jQuery. I have tried replacing them to a pure DOM implementation and have come up with the following code:
function doSomethingWithSelectedText(obj) {
var textObj = getSelectedTextObj();
if (textObj.toString() && textObj.toString().trim().split(" ").length <= 2) {
var NewPara = document.createElement("div");
NewPara.setAttribute("id", "infoDiv");
NewPara.setAttribute("class", "tooltipDiv");
NewPara.appendChild(document.createTextNode(textObj.toString().trim()));
if (document.getElementsByClassName("tooltipDiv").length) {
var OldPara = document.getElementById("infoDiv");
document.body.replaceChild(NewPara, OldPara);
}
else {
document.body.appendChild(NewPara);
}
document.getElementById("infoDiv").style.display = "block";
document.getElementById("infoDiv").style.width = "250px";
document.getElementById("infoDiv").style.zIndex = "101";
document.getElementById("infoDiv").style.backgroundColor = "#F5DEB3";
document.getElementById("infoDiv").style.border = "3px solid #666";
document.getElementById("infoDiv").style.padding = "12px 12px 12px 12px";
document.getElementById("infoDiv").style.borderRadius = "0px 0px 25px 0px";
document.getElementById("infoDiv").style.position = "absolute";
var oRect = textObj.getRangeAt(0).getBoundingClientRect();
var leftOffset, topOffset;
var pageWidth, pageHeight;
var w = window, d = document, e = d.documentElement, g = d.getElementsByTagName('body')[0],
pageWidth = w.innerWidth || e.clientWidth || g.clientWidth,
pageHeight = w.innerHeight|| e.clientHeight|| g.clientHeight;
leftOffset = oRect.left + oRect.width + 1 + 12;
if (300 + leftOffset > pageWidth - 12) {
leftOffset = pageWidth - 350 - 12;
}
topOffset = oRect.top;
if (topOffset + 12 > pageHeight) {
topOffset -= 12;
}
leftOffset += window.scrollX;
topOffset += window.scrollY;
document.getElementById("infoDiv").style.left = leftOffset;
document.getElementById("infoDiv").style.top = topOffset;
}
else {
document.getElementById("infoDiv").style.display = "none";
};
};
The issue with the above code is that the popup is not displayed next to the text which is double clicked, instead it sometimes appears below and sometimes above the screen. I have tried positioning div at various places and replacing clientX
, clientY
with pageX
, pageY
but nothing seems to work. I strongly believe that the problem is with the node NewPara
and it's position, but I am unable to determine it.
UPDATE: Modified the code but initial problem still persists.
There are two major issues which will fix the popup's display position.
First is the document.body
should be replaced with document.documentElement
as to put the scope outside the body
tag and into the html
tag.
The next and major issue is with the leftOffset
and topOffset
. Both of them are never converted to px
, hence they are never rendered by the browser.
To prevent that use String(leftOffset) + "px"
and String(topOffset) + "px"
.
Here is the modified code:
function doSomethingWithSelectedText(obj) {
var textObj = getSelectedTextObj();
if (textObj.toString() && textObj.toString().trim().split(" ").length <= 2) {
var NewPara = document.createElement("div");
NewPara.setAttribute("id", "infoDiv");
NewPara.setAttribute("class", "tooltipDiv");
NewPara.appendChild(document.createTextNode(textObj.toString().trim()));
if (document.getElementsByClassName("tooltipDiv").length) {
var OldPara = document.getElementById("infoDiv");
document.documentElement.replaceChild(NewPara, OldPara);
}
else {
document.documentElement.appendChild(NewPara);
}
var oRect = textObj.getRangeAt(0).getBoundingClientRect();
var leftOffset, topOffset;
var pageWidth, pageHeight;
var w = window, d = document, e = d.documentElement, g = d.getElementsByTagName('body')[0],
pageWidth = w.innerWidth || e.clientWidth || g.clientWidth,
pageHeight = w.innerHeight|| e.clientHeight|| g.clientHeight;
leftOffset = oRect.left + oRect.width + 1 + 12;
if (300 + leftOffset > pageWidth - 12) {
leftOffset = pageWidth - 350 - 12;
}
topOffset = oRect.top;
if (topOffset + 12 > pageHeight) {
topOffset -= 12;
}
leftOffset += window.scrollX;
topOffset += window.scrollY;
document.getElementById("infoDiv").style.display = "block";
document.getElementById("infoDiv").style.width = "250px";
document.getElementById("infoDiv").style.zIndex = "101";
document.getElementById("infoDiv").style.backgroundColor = "#F5DEB3";
document.getElementById("infoDiv").style.border = "3px solid #666";
document.getElementById("infoDiv").style.padding = "12px 12px 12px 12px";
document.getElementById("infoDiv").style.borderRadius = "0px 0px 25px 0px";
document.getElementById("infoDiv").style.position = "absolute";
document.getElementById("infoDiv").style.left = String(leftOffset) + "px";
document.getElementById("infoDiv").style.top = String(topOffset) + "px";
}
else {
document.getElementById("infoDiv").style.display = "none";
};
};
This fixes the position issue.