skip to Main Content

I know, there’s Fixed Div Overflow is Off Screen, I have a similar but still different problem: my DIV isn’t always 100% of the page’s height, so bottom:0px won’t work for me.
I have a DIV fixed to the top right corner of the page which has some contents, one of them is another div with a variable amount of content. Sometimes the content gets too much and both DIVs expand to contain it – it goes over the edge of the page and doesn’t scroll. Here’s my code (there’s only JS code because the JavaScript generates the HTML and CSS as a widget):

// The plain text in this function (except error messages) is in German because it's for my German website. 
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:''}) { // Einfach msgTimeout=-1 setzen, um eine permanente Nachricht zu erstellen.
        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 isn't of type "function" or "string" (is of type "${typeof options.onclickaction}")!`);
            }
        } catch (e) {
            console.warn('Couldn't assign onclickaction to message, ignoring that.');
            console.warn(e);
        }
        // msgElmnt.style = 'pointer-events:auto;';
        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'; // Nur wenn eine Nachrichtenanzeigedauer existiert: flex-wrap auf 'wrap' stellen.
        }
        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;
            }); // Nachricht nach Ablauf der Animation entfernen
        }
        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() { /* Unscharf und durchsichtig animieren */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; // Durch drei, weil jede Nachricht ein DIV-Element mit zwei DIV-Elementen ineinander darin ist.
        return msgElmnt;
    }
    const nwClose = document.createElement('button');
    const nwOpen = document.createElement('button');
    nwClose.id = 'nwClose';
    nwClose.hidden = true;
    nwClose.innerText = 'xa0Xxa0'; // xa0 entspricht der HTML-Entität  
    nwClose.title = 'Nachrichtenwidget ausblenden';
    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 = 'Nachrichtenwidget einblenden';
    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 = 'Seitennachrichten';
    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;
}

// Spawn the widget: 
notifWidg = nachrichtenwidgetAnzeigen();
notifWidg.addMsg('This is a notification widget. You can close any notification by clicking on it.', 'https://www.lampe2020.de/favicon.ico', -1);

// Just to spawn some messages to show what happens: 
setInterval(() => {
  notifWidg.addMsg('Test notification', undefined, 10_000)
}, 1_000);
/*
The main div:
div#nachrichtenwidget {
    position:fixed;
    top:0px;
    right:0px;
    max-width:33%;
    max-height: calc(100%-3px);
    ...
}

The div with the variable content:
div#nachrichten {
    overflow-x:clip;
    overflow-y:auto;
    ...
*/

image of what I get with my old code vs. what I want (photoshopped because it doesn’t work in its current state)

2

Answers


  1. You can use the VH unit (1% of the viewport’s height.) If you set height: 100vh to your right window, you can set overflow: auto, which will add the scroll bar when your content exceeds the viewport height.

    Of course, you can continue using calc() with VH units to adjust the position/size of your overlapping container. You can also set max-height instead of height.

    Another option is to have one "overlap" container, which has a height of 100vh and is fixed, but also has pointer-events: none. Then, it’s children (.overlap > *) will have pointer-events: auto. That way, users can click behind the overlap when there is no content inside. Still, I would suggest using just max-height.

    .sticky {
        width: 200px;
        height: 100vh;
        position: fixed;
        top: 0;
        right: 0;
        overflow: auto;
    }
    
    .sticky div {
        background: red;
        margin-bottom: 5px;
        height: 100px;
    }
    <p>Scroll the right boxes.</p>
    
    <div class="sticky">
        <div></div>
        <div></div>
        <div></div>
        <div></div>
        <div></div>
        <div></div>
        <div></div>
        <div></div>
        <div></div>
    </div>

    Of course, you can have your inner div being the scrollable element 👍 That means something like this:

    .sticky {
        width: 200px;
        height: 100vh;
        position: fixed;
        top: 0;
        right: 0;
      
      display: flex;
      flex-direction: column;
    }
    
    .overflow {
        overflow: auto;
      flex: 1 1 auto;
    }
    
    .overflow div {
        background: red;
        margin-bottom: 5px;
        height: 100px;
    }
    <div class="sticky">
      <h2>Heading</h2>
        <div class="overflow">
        <div></div>
        <div></div>
        <div></div>
        <div></div>
        <div></div>
        <div></div>
        <div></div>
        <div></div>
        <div></div>
      </div>
    </div>
    Login or Signup to reply.
  2. Adding a

    max-height: 90vh;

    to the element with id ‘nachrichten’

    // The plain text in this function (except error messages) is in German because it's for my German website. 
    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;max-height:90vh';
        nw.nachrichten.addMsg = nw.addMsg = function(msg, iconURL=undefined, msgTimeout=10_000, options={color:'white',bgColor:'blue',onclickaction:''}) { // Einfach msgTimeout=-1 setzen, um eine permanente Nachricht zu erstellen.
            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 isn't of type "function" or "string" (is of type "${typeof options.onclickaction}")!`);
                }
            } catch (e) {
                console.warn('Couldn't assign onclickaction to message, ignoring that.');
                console.warn(e);
            }
            // msgElmnt.style = 'pointer-events:auto;';
            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'; // Nur wenn eine Nachrichtenanzeigedauer existiert: flex-wrap auf 'wrap' stellen.
            }
            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;
                }); // Nachricht nach Ablauf der Animation entfernen
            }
            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() { /* Unscharf und durchsichtig animieren */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; // Durch drei, weil jede Nachricht ein DIV-Element mit zwei DIV-Elementen ineinander darin ist.
            return msgElmnt;
        }
        const nwClose = document.createElement('button');
        const nwOpen = document.createElement('button');
        nwClose.id = 'nwClose';
        nwClose.hidden = true;
        nwClose.innerText = 'xa0Xxa0'; // xa0 entspricht der HTML-Entität &nbsp;
        nwClose.title = 'Nachrichtenwidget ausblenden';
        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 = 'Nachrichtenwidget einblenden';
        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 = 'Seitennachrichten';
        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;
    }
    
    // Spawn the widget: 
    notifWidg = nachrichtenwidgetAnzeigen();
    notifWidg.addMsg('This is a notification widget. You can close any notification by clicking on it.', 'https://www.lampe2020.de/favicon.ico', -1);
    
    // Just to spawn some messages to show what happens: 
    setInterval(() => {
      notifWidg.addMsg('Test notification', undefined, 10_000)
    }, 500);

    The thing was the inner div was not overflowing, it was just growing out of the visible screen. If you zoom out you will still be able to see the content of the divs. The max height in the inner div will prevent it from growing any further than the height of the view port causing an overflow.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search