最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - scrollTop for div with image does not scroll to the top of the image - Stack Overflow

programmeradmin2浏览0评论

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?

Share Improve this question edited yesterday Sedenion asked yesterday SedenionSedenion 6,1232 gold badges16 silver badges47 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

I 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.

发布评论

评论列表(0)

  1. 暂无评论