async function main() {
const numStudents = Number(await new Modal('a\n\na\n\na\n\na\n\na\n\na', 'info', 'How many students do you have?', 'Cancel', 'Submit').response());
const numGrades = Number(await new Modal('', 'info', 'How many grades does each student have?', 'Cancel', 'Submit').response());
}
class Modal {
constructor(bodyText, type = 'info', headerText = null, footerText = 'Close', prompt = null, closeable = true) {
this.type = type;
if (headerText === null) {
switch (this.type) {
case 'success':
this.headerText = 'Success!';
break;
case 'info':
this.headerText = 'Information';
break;
case 'warning':
this.headerText = 'Warning!'
break;
case 'error':
this.headerText = 'An error has occurred';
break;
default:
this.headerText = 'Notification';
}
} else {
this.headerText = headerText;
}
this.bodyText = bodyText;
this.footerText = footerText;
this.closeable = closeable;
this.prompt = prompt;
this.create();
this.open();
}
create() {
this.dialog = document.createElement('dialog');
const header = document.createElement('header');
header.classList.add(this.type, 'background');
if (this.closeable) {
const closeButton = document.createElement('button');
closeButton.classList.add('close');
closeButton.innerText = '×';
header.appendChild(closeButton);
}
const headerText = document.createElement('h3');
headerText.innerText = this.headerText;
header.appendChild(headerText);
this.dialog.appendChild(header);
const form = document.createElement('form');
form.method = 'dialog';
const body = document.createElement('main');
this.bodyText.split('\n\n').forEach((paragraph) => {
const p = document.createElement('p');
p.innerText = paragraph;
body.appendChild(p);
});
if (this.prompt !== null) {
this.input = document.createElement('input');
this.input.placeholder = ' ';
this.input.autofocus = true;
const p = document.createElement('p');
p.appendChild(this.input);
body.appendChild(p);
}
form.appendChild(body);
const footer = document.createElement('footer');
footer.classList.add(this.type, 'text');
const hiddenSubmitButton = document.createElement('button');
hiddenSubmitButton.value = 'submit';
hiddenSubmitButton.hidden = true;
footer.appendChild(hiddenSubmitButton);
const closeButton = document.createElement('button');
closeButton.classList.add(this.type, 'text', 'animated');
closeButton.innerText = this.footerText;
footer.appendChild(closeButton);
if (this.prompt === null) {
closeButton.autofocus = true;
} else {
const submitButton = document.createElement('button');
submitButton.classList.add(this.type, 'background', 'animated');
submitButton.innerText = this.prompt;
submitButton.value = 'submit';
footer.appendChild(submitButton);
}
form.appendChild(footer);
this.dialog.addEventListener('close', (event) => {
this.close(event.target.returnValue);
});
this.dialog.appendChild(form);
document.body.appendChild(this.dialog);
}
open() {
this.dialog.showModal();
}
close(returnValue) {
if (this.prompt !== null) {
if (returnValue === '') {
this.responseValue = null;
if (this.rejectPromise !== undefined) {
this.rejectPromise('User canceled prompt');
}
} else {
this.responseValue = this.input.value;
if (this.rejectPromise !== undefined) {
this.resolvePromise(this.responseValue);
}
}
}
}
response() {
this.promise = new Promise((resolve, reject) => {
if (this.responseValue !== undefined) {
if (this.responseValue === null) {
reject('User canceled prompt')
} else {
resolve(this.responseValue);
}
} else {
this.resolvePromise = resolve;
this.rejectPromise = reject;
}
});
return this.promise;
}
}
main();
:root {
--error: #c00;
--error-dark: #900;
--error-light: #f00;
--info: #36c;
--info-dark: #039;
--info-light: #69f;
--muted: #ddd;
--muted-dark: #888;
--muted-light: #eee;
--success: #0c0;
--success-dark: #090;
--success-light: #0f0;
--warning: #cc0;
--warning-dark: #990;
--warning-light: #ff0;
}
body {
font-family: Arial, Helvetica, sans-serif;
}
button {
border: 2px solid;
border-radius: 10px;
cursor: pointer;
margin: 1em 0.5em;
padding: 10px 15px;
transition: transform 1s;
}
button:active {
transform: scale(90%);
}
button.animated {
background-color: transparent;
overflow: hidden;
position: relative;
transition: color 0.3s, border-color 0.3s, transform 0.2s;
z-index: 1;
}
button.animated:hover {
border-color: transparent;
}
button.animated::after {
border: 0;
border-radius: 50%;
content: "";
height: 200%;
left: -50%;
opacity: 0;
position: absolute;
transform: scale(0.1);
transform-origin: center;
transition: opacity 0.3s, transform 0.3s;
top: -50%;
width: 200%;
z-index: -1;
}
button.animated:hover::after {
opacity: 1;
transform: scale(1);
}
input {
border: 0;
font: inherit;
letter-spacing: normal;
margin: 0;
padding: 0;
}
input:focus, input:placeholder-shown {
box-shadow: 0 2px 0 var(--muted);
outline: none;
}
dialog {
background-color: white;
border: 0;
border-radius: 10px;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
opacity: 0;
outline: none;
padding: 0;
transform: scale(0);
transition: all 0.4s allow-discrete;
width: 50%;
}
dialog:open {
opacity: 1;
transform: scale(1);
}
@starting-style {
dialog:open {
opacity: 0;
transform: scale(0);
}
}
dialog::backdrop {
background-color: rgba(0, 0, 0, 0);
transition: all 0.4s allow-discrete;
}
dialog:open::backdrop {
background-color: rgba(0, 0, 0, 0.4);
}
@starting-style {
dialog:open::backdrop {
background-color: rgba(0, 0, 0, 0);
}
}
dialog header, dialog main, dialog footer {
display: flow-root;
padding: 0 1em;
text-align: center;
}
dialog header {
background-color: black;
color: white;
}
dialog main, dialog footer {
background-color: white;
color: black;
}
dialog form {
display: flex;
flex-direction: column;
}
dialog header, dialog footer {
flex: initial;
}
dialog main {
flex: 1 1 auto;
overflow-y: auto;
}
.close {
aspect-ratio: 1 / 1;
background-color: rgba(0, 0, 0, 0);
border: 0;
border-radius: 50%;
box-sizing: border-box;
color: var(--muted);
font-size: 1.2em;
font-weight: bold;
height: 1.2em;
margin: 0;
padding: 0;
position: absolute;
right: 0.5rem;
top: 0.5rem;
user-select: none;
}
.close:hover,
.close:focus {
background-color: rgba(255, 255, 255, 0.2);
color: white;
}
.success.text, .info.text, .warning.text, .error.text, button.animated.success.background::after, button.animated.info.background::after, button.animated.warning.background::after, button.animated.error.background::after {
background-color: white;
}
.success.background, .info.background, warning.background, error.background, button.animated.success.text:hover, button.animated.info.text:hover, button.animated.warning.text:hover, button.animated.error.text:hover {
color: white;
}
button.animated.success.text, button.animated.info.text, button.animated.warning.text, button.animated.error.text {
border-color: var(--muted);
}
.success.text, button.animated.success.background:hover {
color: var(--success);
}
.success.background, button.animated.success.text::after {
background-color: var(--success);
}
button.animated.success.text:hover, button.animated.success.background {
border-color: var(--success);
}
.info.text, button.animated.info.background:hover {
color: var(--info);
}
.info.background, button.animated.info.text::after {
background-color: var(--info);
}
button.animated.info.text:hover, button.animated.info.background {
border-color: var(--info);
}
.warning.text, button.animated.warning.background:hover {
color: var(--warning);
}
.warning.background, button.animated.warning.text::after {
background-color: var(--warning);
}
button.animated.warning.text:hover, button.animated.warning.background {
border-color: var(--warning);
}
.error.text, button.animated.error.background:hover {
color: var(--error);
}
.error.background, button.animated.error.text::after {
background-color: var(--error);
}
button.animated.error.text:hover, button.animated.error.background {
border-color: var(--error);
}
I have a modal, as you can easily see in the link. When you resize the window until a scroll bar appears, you will see that the scroll bar appears for the entire modal. If you look closely, you can observe that the modal consists of a header (obviously), a main (the body text), and a footer (the button(s) at the bottom). I want the header and footer to act like a header and footer by only having the main portion have a scroll bar. How do I do this?
Since your grid is mainly a column layout , you may use grid instead flex and set the rows to auto and 1fr for the one supposed to fill the remaining space and scroll if needed.
Below a snippet with a couple of grid and overflow rules to make main scroll:
async function main() {
const numStudents = Number(await new Modal('a\n\na\n\na\n\na\n\na\n\na', 'info', 'How many students do you have?', 'Cancel', 'Submit').response());
const numGrades = Number(await new Modal('', 'info', 'How many grades does each student have?', 'Cancel', 'Submit').response());
}
class Modal {
constructor(bodyText, type = 'info', headerText = null, footerText = 'Close', prompt = null, closeable = true) {
this.type = type;
if (headerText === null) {
switch (this.type) {
case 'success':
this.headerText = 'Success!';
break;
case 'info':
this.headerText = 'Information';
break;
case 'warning':
this.headerText = 'Warning!'
break;
case 'error':
this.headerText = 'An error has occurred';
break;
default:
this.headerText = 'Notification';
}
} else {
this.headerText = headerText;
}
this.bodyText = bodyText;
this.footerText = footerText;
this.closeable = closeable;
this.prompt = prompt;
this.create();
this.open();
}
create() {
this.dialog = document.createElement('dialog');
const header = document.createElement('header');
header.classList.add(this.type, 'background');
if (this.closeable) {
const closeButton = document.createElement('button');
closeButton.classList.add('close');
closeButton.innerText = '×';
header.appendChild(closeButton);
}
const headerText = document.createElement('h3');
headerText.innerText = this.headerText;
header.appendChild(headerText);
this.dialog.appendChild(header);
const form = document.createElement('form');
form.method = 'dialog';
const body = document.createElement('main');
this.bodyText.split('\n\n').forEach((paragraph) => {
const p = document.createElement('p');
p.innerText = paragraph;
body.appendChild(p);
});
if (this.prompt !== null) {
this.input = document.createElement('input');
this.input.placeholder = ' ';
this.input.autofocus = true;
const p = document.createElement('p');
p.appendChild(this.input);
body.appendChild(p);
}
form.appendChild(body);
const footer = document.createElement('footer');
footer.classList.add(this.type, 'text');
const hiddenSubmitButton = document.createElement('button');
hiddenSubmitButton.value = 'submit';
hiddenSubmitButton.hidden = true;
footer.appendChild(hiddenSubmitButton);
const closeButton = document.createElement('button');
closeButton.classList.add(this.type, 'text', 'animated');
closeButton.innerText = this.footerText;
footer.appendChild(closeButton);
if (this.prompt === null) {
closeButton.autofocus = true;
} else {
const submitButton = document.createElement('button');
submitButton.classList.add(this.type, 'background', 'animated');
submitButton.innerText = this.prompt;
submitButton.value = 'submit';
footer.appendChild(submitButton);
}
form.appendChild(footer);
this.dialog.addEventListener('close', (event) => {
this.close(event.target.returnValue);
});
this.dialog.appendChild(form);
document.body.appendChild(this.dialog);
}
open() {
this.dialog.showModal();
}
close(returnValue) {
if (this.prompt !== null) {
if (returnValue === '') {
this.responseValue = null;
if (this.rejectPromise !== undefined) {
this.rejectPromise('User canceled prompt');
}
} else {
this.responseValue = this.input.value;
if (this.rejectPromise !== undefined) {
this.resolvePromise(this.responseValue);
}
}
}
}
response() {
this.promise = new Promise((resolve, reject) => {
if (this.responseValue !== undefined) {
if (this.responseValue === null) {
reject('User canceled prompt')
} else {
resolve(this.responseValue);
}
} else {
this.resolvePromise = resolve;
this.rejectPromise = reject;
}
});
return this.promise;
}
}
main();
:root {
--error: #c00;
--error-dark: #900;
--error-light: #f00;
--info: #36c;
--info-dark: #039;
--info-light: #69f;
--muted: #ddd;
--muted-dark: #888;
--muted-light: #eee;
--success: #0c0;
--success-dark: #090;
--success-light: #0f0;
--warning: #cc0;
--warning-dark: #990;
--warning-light: #ff0;
}
body {
font-family: Arial, Helvetica, sans-serif;
}
button {
border: 2px solid;
border-radius: 10px;
cursor: pointer;
margin: 1em 0.5em;
padding: 10px 15px;
transition: transform 1s;
}
button:active {
transform: scale(90%);
}
button.animated {
background-color: transparent;
overflow: hidden;
position: relative;
transition: color 0.3s, border-color 0.3s, transform 0.2s;
z-index: 1;
}
button.animated:hover {
border-color: transparent;
}
button.animated::after {
border: 0;
border-radius: 50%;
content: "";
height: 200%;
left: -50%;
opacity: 0;
position: absolute;
transform: scale(0.1);
transform-origin: center;
transition: opacity 0.3s, transform 0.3s;
top: -50%;
width: 200%;
z-index: -1;
}
button.animated:hover::after {
opacity: 1;
transform: scale(1);
}
input {
border: 0;
font: inherit;
letter-spacing: normal;
margin: 0;
padding: 0;
}
input:focus,
input:placeholder-shown {
box-shadow: 0 2px 0 var(--muted);
outline: none;
}
dialog {
background-color: white;
border: 0;
border-radius: 10px;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
opacity: 0;
outline: none;
padding: 0;
transform: scale(0);
transition: all 0.4s allow-discrete;
width: 50%;
display: grid;
grid-template-rows: auto 1fr;
overflow: hidden;
min-height:12em;
}
dialog:open {
opacity: 1;
transform: scale(1);
}
@starting-style {
dialog:open {
opacity: 0;
transform: scale(0);
}
}
dialog::backdrop {
background-color: rgba(0, 0, 0, 0);
transition: all 0.4s allow-discrete;
}
dialog:open::backdrop {
background-color: rgba(0, 0, 0, 0.4);
}
@starting-style {
dialog:open::backdrop {
background-color: rgba(0, 0, 0, 0);
}
}
dialog header,
dialog main,
dialog footer {
display: flow-root;
padding: 0 1em;
text-align: center;
}
dialog header {
background-color: black;
color: white;
}
dialog main,
dialog footer {
background-color: white;
color: black;
}
dialog form {
display: grid;
grid-template-rows: 1fr auto;
overflow: hidden;
}
dialog header,
dialog footer {
flex: initial;
}
dialog main {
overflow-y: auto;
}
.close {
aspect-ratio: 1 / 1;
background-color: rgba(0, 0, 0, 0);
border: 0;
border-radius: 50%;
box-sizing: border-box;
color: var(--muted);
font-size: 1.2em;
font-weight: bold;
height: 1.2em;
margin: 0;
padding: 0;
position: absolute;
right: 0.5rem;
top: 0.5rem;
user-select: none;
}
.close:hover,
.close:focus {
background-color: rgba(255, 255, 255, 0.2);
color: white;
}
.success.text,
.info.text,
.warning.text,
.error.text,
button.animated.success.background::after,
button.animated.info.background::after,
button.animated.warning.background::after,
button.animated.error.background::after {
background-color: white;
}
.success.background,
.info.background,
warning.background,
error.background,
button.animated.success.text:hover,
button.animated.info.text:hover,
button.animated.warning.text:hover,
button.animated.error.text:hover {
color: white;
}
button.animated.success.text,
button.animated.info.text,
button.animated.warning.text,
button.animated.error.text {
border-color: var(--muted);
}
.success.text,
button.animated.success.background:hover {
color: var(--success);
}
.success.background,
button.animated.success.text::after {
background-color: var(--success);
}
button.animated.success.text:hover,
button.animated.success.background {
border-color: var(--success);
}
.info.text,
button.animated.info.background:hover {
color: var(--info);
}
.info.background,
button.animated.info.text::after {
background-color: var(--info);
}
button.animated.info.text:hover,
button.animated.info.background {
border-color: var(--info);
}
.warning.text,
button.animated.warning.background:hover {
color: var(--warning);
}
.warning.background,
button.animated.warning.text::after {
background-color: var(--warning);
}
button.animated.warning.text:hover,
button.animated.warning.background {
border-color: var(--warning);
}
.error.text,
button.animated.error.background:hover {
color: var(--error);
}
.error.background,
button.animated.error.text::after {
background-color: var(--error);
}