You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让非满高的position:fixed容器超出页面高度时可滚动

固定右上角通知组件内容溢出问题解决

我了解已有《Fixed Div Overflow is Off Screen》相关问题,但我的场景有所不同:我的DIV并非始终占满页面高度,因此bottom:0px方案不适用。我有一个固定在页面右上角的DIV,内部包含一个内容量可变的子DIV,当内容过多时,两个DIV会被撑开并超出页面边缘,且无法滚动。

原组件代码

JavaScript代码

// 函数内的普通文本为德语,适配我的德语网站
function nachrichtenwidgetAnzeigen() {
    const nw = document.createElement('div');
    nw.id = 'nachrichtenwidget';
    nw.hidden = true;
    nw.style = 'position:fixed;top:0px;right:0px;max-height:calc(100%-3px);max-width:33%;border-left:1px solid blue;border-bottom:1px solid blue;border-bottom-left-radius:6px;background-color:rgba(0,255,0,0.3);color:rgba(0,0,0,0);/*pointer-events:none;opacity:80%;*/';
    nw.nachrichten = document.createElement('div');
    nw.nachrichten.id = 'nachrichten';
    nw.nachrichten.style = 'color:white;background-color:rgba(0,0,0,0);overflow-x: clip;overflow-y:auto;';
    nw.nachrichten.addMsg = nw.addMsg = function(msg, iconURL=undefined, msgTimeout=10_000, options={color:'white',bgColor:'blue',onclickaction:''}) { // 设置msgTimeout=-1可创建永久消息
        const msgElmnt = document.createElement('div');
        try {
            if (typeof options.onclickaction === 'function') {
                msgElmnt.addEventListener('click', (e) => {
                    msgElmnt.close();
                    options.onclickaction();
                });
            } else if (typeof options.onclickaction === 'string') {
                msgElmnt.addEventListener('click', (e) => {
                    msgElmnt.close();
                    Function(options.onclickaction)();
                });
            } else {
                throw new TypeError(`options.onclickaction 类型错误,应为"function"或"string",当前类型为"${typeof options.onclickaction}"!`);
            }
        } catch (e) {
            console.warn('无法为消息绑定点击事件,忽略该设置');
            console.warn(e);
        }
        if (iconURL!==undefined) {
            msgElmnt.msgImg = document.createElement('img');
            msgElmnt.msgImg.style = 'max-width:33px;max-height:33px;padding-right:3px;';
            msgElmnt.msgImg.src = iconURL;
            msgElmnt.appendChild(msgElmnt.msgImg);
        } else {
            msgElmnt.msgImg = undefined;
        }
        msgElmnt.innerHTML += msg;
        msgElmnt.id = `msg-${msg.replace(/[^a-z0-9]/gi, '-').toLowerCase()}`;
        msgElmnt.style = 'background-color:blue;color:white;padding:3px;margin:3px;border:0px solid blue;border-radius:3px;display:flex;flex-direction:row;';
        if (msgTimeout >= 0) {
            msgElmnt.style.flexWrap = 'wrap'; // 仅当设置消息显示时长时,开启自动换行
        }
        msgElmnt.timeout = document.createElement('div');
        msgElmnt.timeout.style = 'width:100%;height:3px;border:0px solid blue;border-radius:3px;color:rgba(0,0,0,0);background-color:grey;flex:0 0 100%;';
        msgElmnt.timeout.bar = document.createElement('div');
        msgElmnt.timeout.bar.style = 'width:100%;height:3px;border:0px solid blue;border-radius:3px;color:rgba(0,0,0,0);background-color:lightblue;';
        async function animTimeout(e) {
            msgElmnt.timeout.bar.animate([
                { width: '100%' },
                { width: '0%' }
            ], msgTimeout);
            msgElmnt.timeoutAnimation = msgElmnt.timeout.bar.getAnimations()[0];
            msgElmnt.timeoutAnimation.addEventListener('finish', (e) => {
                msgElmnt.remove();
                nw.countElmnt.innerText = nw.nachrichten.getElementsByTagName('div').length/3;
            }); // 动画结束后移除消息
        }
        msgElmnt.addEventListener('mouseover', (e) => {
            if (msgElmnt.timeoutAnimation) {
                msgElmnt.timeoutAnimation.pause();
            }
            msgElmnt.style.border = '1px solid white';
            msgElmnt.style.padding = '2px';
        });
        msgElmnt.addEventListener('mouseout', (e) => {
            if (msgElmnt.timeoutAnimation) {
                msgElmnt.timeoutAnimation.play();
            }
            msgElmnt.style.border = '0px solid blue';
            msgElmnt.style.padding = '3px';
        });
        if (msgTimeout>=0) { msgElmnt.timeout.bar.addEventListener('click', animTimeout); } else { msgElmnt.timeout.hidden = true; }
        msgElmnt.close = function() { /* 添加模糊透明动画 */msgElmnt.remove(); nw.countElmnt.innerText = nw.nachrichten.getElementsByTagName('div').length/3; };
        msgElmnt.timeout.appendChild(msgElmnt.timeout.bar);
        msgElmnt.timeout.bar.click();
        msgElmnt.timeout.bar.removeEventListener('click', animTimeout);
        msgElmnt.appendChild(msgElmnt.timeout);
        nw.nachrichten.appendChild(msgElmnt);
        nw.countElmnt.innerText = nw.nachrichten.getElementsByTagName('div').length/3; // 除以3是因为每条消息包含1个外层DIV和2个嵌套DIV
        return msgElmnt;
    }
    const nwClose = document.createElement('button');
    const nwOpen = document.createElement('button');
    nwClose.id = 'nwClose';
    nwClose.hidden = true;
    nwClose.innerText = '\xa0X\xa0'; // \xa0对应HTML实体 
    nwClose.title = '隐藏消息组件';
    nwClose.style = 'background-color:red;border-radius:10px;';
    nwClose.addEventListener('click', (e) => {
        nwClose.hidden = true;
        nw.hidden = true;
        nwOpen.hidden = false;
    });
    nwOpen.id = 'nwOpen';
    nwOpen.hidden = false;
    nwOpen.title = '显示消息组件';
    nwOpen.style = 'position:fixed;top:0px;right:33px;background-color:green;';
    nwOpen.addEventListener('click', (e) => {
        nwOpen.hidden = true;
        nw.hidden = false;
        nwClose.hidden = false;
    });
    const nwTitle = document.createElement('span');
    nwTitle.style = 'color:white;background-color:rgba(0,0,0,0);padding-left:3px;user-select:none;';
    nwTitle.innerText = '站点消息';
    nw.countElmnt = document.createElement('span');
    nw.countElmnt.style = 'color:white;background-color:rgba(0,0,0,0);padding:3px;user-select:none;';
    document.body.appendChild(nw);
    nwOpen.appendChild(nw.countElmnt);
    document.body.appendChild(nwOpen);
    nw.appendChild(nwClose);
    nw.appendChild(nwTitle);
    nw.appendChild(nw.nachrichten);
    nwOpen.click();
    return nw;
}

// 生成组件: 
notifWidg = nachrichtenwidgetAnzeigen();
notifWidg.addMsg('这是一个通知组件,点击任意通知即可关闭它', 'https://www.lampe2020.de/favicon.ico', -1);

// 生成测试消息,展示问题: 
setInterval(() => {
  notifWidg.addMsg('测试通知', undefined, 10_000)
}, 1_000);

核心CSS逻辑(JS内联)

/*
主容器:
div#nachrichtenwidget {
    position:fixed;
    top:0px;
    right:0px;
    max-width:33%;
    max-height: calc(100%-3px);
    ...
}

内容容器:
div#nachrichten {
    overflow-x:clip;
    overflow-y:auto;
    ...
*/

问题分析

  1. calc语法错误:max-height: calc(100%-3px)缺少空格,浏览器无法正确解析该计算值,导致主容器高度不受限制,随内容无限撑开。
  2. 内容容器无高度约束:子容器#nachrichten没有设置高度填充规则,即使主容器高度正确,子容器也会跟着内容溢出,无法触发滚动。

修复方案

修改两处关键样式:

  1. 主容器样式修改:
    修复calc语法错误,同时添加flex布局,让子容器可以填充剩余空间:
    nw.style = 'position:fixed;top:0px;right:0px;max-height:calc(100% - 3px);max-width:33%;border-left:1px solid blue;border-bottom:1px solid blue;border-bottom-left-radius:6px;background-color:rgba(0,255,0,0.3);color:rgba(0,0,0,0);display:flex;flex-direction:column;';
    
  2. 内容容器样式修改:
    添加flex:1,让容器填充主容器的剩余高度,触发滚动:
    nw.nachrichten.style = 'color:white;background-color:rgba(0,0,0,0);overflow-x: clip;overflow-y:auto;flex:1;';
    

效果对比

旧代码效果与期望效果对比(右侧为期望效果,修复后可实现)

内容的提问来源于stack exchange,提问作者Lampe2020

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 13:20:51