I was able to achieve a footnote “system” that displays a popup at the bottom of the window (as mentioned in another recent post). My current code works, but I have to scroll the page or click the “x” inside each popup to close the bottom popup.
I wonder if and how I can click to close the popup anywhere (not just above the ‘x’ in the popup, not at all)?
I tried adding window.addEventListener("click", handler("close"));
after (or even replacing) window.addEventListener("scroll", handler("close"));
but failed.
Here is my entire js code:
function getFootnoteContent(index) {
const id = "fn:" + index;
const fn = document.getElementById(id);
return fn.innerHTML.trim();
};
function footnotePopup(showIndex, showCloseBtn) {
const popupWrapper = document.querySelector("#popup-wrapper");
// Set whether to display index and/or close button. Default is true for both
if (showIndex === undefined) {
showIndex = true;
}
if (showCloseBtn === undefined) {
showCloseBtn = false;
}
// Create main container that will hold footnote content
const popupContent = popupWrapper.appendChild(document.createElement("div"));
popupContent.id = "popup-content";
let popupIndex = null;
if (showIndex) {
popupIndex = popupWrapper.insertBefore(document.createElement("div"), popupContent);
popupIndex.id = "popup-index";
}
let popupCloseButton = null;
if (showCloseBtn) {
popupCloseButton = popupWrapper.appendChild(document.createElement("div"));
popupCloseButton.innerHTML = "[x]";
popupCloseButton.id = "popup-close";
}
// Remove redundant [return] links from footnote list (optional)
const fnReturns = document.querySelectorAll("a.footnote-return");
fnReturns.forEach(function(fnReturn) {
const parent = fnReturn.parentNode;
parent.removeChild(fnReturn);
});
const fnRefs = document.querySelectorAll("a[id^='fnref:']");
fnRefs.forEach(function(fnRef) {
fnRef.addEventListener("click", handler("refs", fnRef));
});
window.addEventListener("scroll", handler("close"));
//window.addEventListener("click", handler("close"));
if (showCloseBtn) {
popupCloseButton.addEventListener("click", handler("close"));
}
function handler(type, node) {
return function(event) {
if (type === "close") {
popupWrapper.style.display = "none";
}
if (type === "refs") {
event.preventDefault();
const index = node.id.substring(6);
if (showIndex) {
popupIndex.innerHTML = index + ".";
}
popupContent.innerHTML = getFootnoteContent(index);
popupWrapper.style.display = "flex";
}
};
};
};
footnotePopup(false, true);
thank you!
I believe that detecting a click event in the page body or HTML will give you the necessary hooks to perform the action. Note that events bubble up to higher level elements.
1 like
thank you. However, my knowledge of JavaScript is too poor to fully understand what I need to change in my code (even with the help of the page you linked to)…
edit
maybe i should use event.stopPropagation();
but how?
you are on the right track.
If a user clicks on a refs link, you should not only prevent the default, but also prevent it from propagating.
Then you can bind the close handler to a higher level object such as the body.
1 like
Too difficult for my knowledge.
and i don’t understand why window.addEventListener("click", handler("close"));
It does not work…
edit
I tried this but it failed:
const fnRefs = document.querySelectorAll("a[id^='fnref:']");
fnRefs.forEach(function(fnRef) {
event.stopPropagation(fnRef);
fnRef.addEventListener("click", handler("refs", fnRef));
});
I noticed that this code somehow works
const fnRefs = document.querySelectorAll("a[id^='fnref:']");
fnRefs.forEach(function(fnRef) {
fnRef.addEventListener("click", handler("refs", fnRef));
});
event.stopPropagation();
But this way I can’t close the popup even if I click on the note reference and it changes as expected.
This might be a bit oversimplified, but it seems to work.
document.addEventListener(
"click",
function (event) {
if (
event.target.matches("a[id^='fnref']") ||
event.target.matches("#popup-content")
) {
return;
}
popupWrapper.style.display = "none";
},
false
);
maybe not the best way
I put it here:
function getFootnoteContent(index) {
const id = "fn:" + index;
const fn = document.getElementById(id);
return fn.innerHTML.trim();
}
function footnotePopup(showIndex, showCloseBtn) {
const popupWrapper = document.querySelector("#popup-wrapper");
// Set whether to display index and/or close button. Default is true for both
if (showIndex === undefined) {
showIndex = true;
}
if (showCloseBtn === undefined) {
showCloseBtn = false;
}
// Create main container that will hold footnote content
const popupContent = popupWrapper.appendChild(document.createElement("div"));
popupContent.id = "popup-content";
let popupIndex = null;
if (showIndex) {
popupIndex = popupWrapper.insertBefore(
document.createElement("div"),
popupContent
);
popupIndex.id = "popup-index";
}
let popupCloseButton = null;
if (showCloseBtn) {
popupCloseButton = popupWrapper.appendChild(document.createElement("div"));
popupCloseButton.innerHTML = "[x]";
popupCloseButton.id = "popup-close";
}
// Remove redundant [return] links from footnote list (optional)
const fnReturns = document.querySelectorAll("a.footnote-return");
fnReturns.forEach(function (fnReturn) {
const parent = fnReturn.parentNode;
parent.removeChild(fnReturn);
});
const fnRefs = document.querySelectorAll("a[id^='fnref:']");
fnRefs.forEach(function (fnRef) {
fnRef.addEventListener("click", handler("refs", fnRef));
});
window.addEventListener("scroll", handler("close"));
document.addEventListener(
"click",
function (event) {
if (
event.target.matches("a[id^='fnref']") ||
event.target.matches("#popup-content")
) {
return;
}
popupWrapper.style.display = "none";
},
false
);
if (showCloseBtn) {
popupCloseButton.addEventListener("click", handler("close"));
}
function handler(type, node) {
console.log("test");
return function (event) {
if (type === "close") {
popupWrapper.style.display = "none";
}
if (type === "refs") {
event.preventDefault();
const index = node.id.substring(6);
if (showIndex) {
popupIndex.innerHTML = index + ".";
}
popupContent.innerHTML = getFootnoteContent(index);
popupWrapper.style.display = "flex";
}
};
}
}
footnotePopup(false, true);
2 likes
very good!
thank you! this is what i was looking for.
I had to check this, but you can match multiple selectors separated by commas.
So in theory you could instead write
if (
event.target.matches("a[id^='fnref'], #popup-content")
)
here a quick test
1 like