I have some piece of html containing an <ins>
element somewhere. I want to show only the <ins>
part and possibly some context above and below. My approach is to use a <div>
with overflow
, setting an appropriate height for the <div>
and scrolling the <div>
to the appropriate location vertically.
The <ins>
might contain images that are higher than the text, and in this case I have trouble getting the scrolling right. This mostly happens if the browser cache is disabled.
As a simple example, assume I have some html with a single <ins>
containing a single <img>
. For simplicity, assume that I want my new overflow <div>
to have the height of the image and that I want to scroll to the top of the image, so that the image vertically fills the <div>
. I compute the top position of the image and scroll to it. I therefore expect to see the full image, but instead I see the top of the <ins>
.
Example (jsfiddle); open the developer tools and disable the browser cache for reproduction!
const blueprintDiv = document.createElement("div");
blueprintDiv.style.cssText = "border: 1px solid; visibility: hidden;";
blueprintDiv.innerHTML = `<p>BEGIN</p><ins>BB<img src=".gif"></ins><p>END1<br><br><br><br><br><br>END2</p>`;
document.body.appendChild(blueprintDiv);
const imgPromises = [Promise.resolve()];
const img = blueprintDiv.querySelector("img");
if (!imgplete) {
imgPromises.push(new Promise(resolve => { img.onload = img.onerror = resolve; }));
}
Promise.all(imgPromises).then(() => {
console.log("All images loaded.");
const newDiv = document.createElement("div");
newDiv.style.cssText = `border: 1px solid red; display: block; overflow: auto; height: ${img?.offsetHeight}px`;
for (const child of blueprintDiv.childNodes) {
newDiv.appendChild(child.cloneNode(true));
}
const insTop = blueprintDiv.querySelector("ins").offsetTop - blueprintDiv.offsetTop;
const imgTop = img.offsetTop - blueprintDiv.offsetTop;
const offsetTop = Math.min(insTop, imgTop);
document.body.textContent = "";
document.body.appendChild(newDiv);
newDiv.scrollTop = offsetTop;
console.log(`Direct ScrollTop: ${newDiv.scrollTop}; offsetTop: ${offsetTop}; insTop: ${insTop}; imgTop: ${imgTop}`);
setTimeout(() => {
console.log(`Delayed ScrollTop: ${newDiv.scrollTop}; offsetTop: ${offsetTop}; insTop: ${insTop}; imgTop: ${imgTop}`);
// newDiv.scrollTop = offsetTop; // Uncomment => correct scrolling
}, 100);
});
The code waits for the image to load, and then creates my new <div>
.
Problem: With the browser cache disabled, instead of scrolling to the top of the image, it scrolls to the BB
text. Moreover, the console output is:
"Direct ScrollTop: 52; offsetTop: 52; insTop: 148; imgTop: 52"
"Delayed ScrollTop: 147; offsetTop: 52; insTop: 148; imgTop: 52"
So, the positions seem right the first time: The <ins>
is at 148, the image top is above at 52. So I set scrollTop
to 52.
But after a short delay, the browser somehow ends up scrolling automatically to the <ins>
position 147 instead of staying at the top of the image at 52.
Uncommenting the second scrollTop
fixes the issue, but that seems like a hack to me. Is there a cleaner way to fix this?
I have some piece of html containing an <ins>
element somewhere. I want to show only the <ins>
part and possibly some context above and below. My approach is to use a <div>
with overflow
, setting an appropriate height for the <div>
and scrolling the <div>
to the appropriate location vertically.
The <ins>
might contain images that are higher than the text, and in this case I have trouble getting the scrolling right. This mostly happens if the browser cache is disabled.
As a simple example, assume I have some html with a single <ins>
containing a single <img>
. For simplicity, assume that I want my new overflow <div>
to have the height of the image and that I want to scroll to the top of the image, so that the image vertically fills the <div>
. I compute the top position of the image and scroll to it. I therefore expect to see the full image, but instead I see the top of the <ins>
.
Example (jsfiddle); open the developer tools and disable the browser cache for reproduction!
const blueprintDiv = document.createElement("div");
blueprintDiv.style.cssText = "border: 1px solid; visibility: hidden;";
blueprintDiv.innerHTML = `<p>BEGIN</p><ins>BB<img src="http://www.google.com/intl/en_ALL/images/logo.gif"></ins><p>END1<br><br><br><br><br><br>END2</p>`;
document.body.appendChild(blueprintDiv);
const imgPromises = [Promise.resolve()];
const img = blueprintDiv.querySelector("img");
if (!img.complete) {
imgPromises.push(new Promise(resolve => { img.onload = img.onerror = resolve; }));
}
Promise.all(imgPromises).then(() => {
console.log("All images loaded.");
const newDiv = document.createElement("div");
newDiv.style.cssText = `border: 1px solid red; display: block; overflow: auto; height: ${img?.offsetHeight}px`;
for (const child of blueprintDiv.childNodes) {
newDiv.appendChild(child.cloneNode(true));
}
const insTop = blueprintDiv.querySelector("ins").offsetTop - blueprintDiv.offsetTop;
const imgTop = img.offsetTop - blueprintDiv.offsetTop;
const offsetTop = Math.min(insTop, imgTop);
document.body.textContent = "";
document.body.appendChild(newDiv);
newDiv.scrollTop = offsetTop;
console.log(`Direct ScrollTop: ${newDiv.scrollTop}; offsetTop: ${offsetTop}; insTop: ${insTop}; imgTop: ${imgTop}`);
setTimeout(() => {
console.log(`Delayed ScrollTop: ${newDiv.scrollTop}; offsetTop: ${offsetTop}; insTop: ${insTop}; imgTop: ${imgTop}`);
// newDiv.scrollTop = offsetTop; // Uncomment => correct scrolling
}, 100);
});
The code waits for the image to load, and then creates my new <div>
.
Problem: With the browser cache disabled, instead of scrolling to the top of the image, it scrolls to the BB
text. Moreover, the console output is:
"Direct ScrollTop: 52; offsetTop: 52; insTop: 148; imgTop: 52"
"Delayed ScrollTop: 147; offsetTop: 52; insTop: 148; imgTop: 52"
So, the positions seem right the first time: The <ins>
is at 148, the image top is above at 52. So I set scrollTop
to 52.
But after a short delay, the browser somehow ends up scrolling automatically to the <ins>
position 147 instead of staying at the top of the image at 52.
Uncommenting the second scrollTop
fixes the issue, but that seems like a hack to me. Is there a cleaner way to fix this?
1 Answer
Reset to default 0I have found that setting a height attribute for an image fixes a problem and scrolls your <div>
to the top of the image instead of the <ins>
position. Hope this helps
Promise.all(imgPromises).then(() => {
console.log("All images loaded.");
img.height = img?.offsetHeight; // setting height like this
// Continuation of your code without any changes
}
Sorry, but I can't clarify or provide any links with relevant information on why it is working.