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 = '\xa0X\xa0'; // \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;
...
*/
CodePudding user response:
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.
- Units (see VH): https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units#numbers_lengths_and_percentages
- Overflow auto: https://developer.mozilla.org/en-US/docs/Web/CSS/overflow
.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 >
<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