Skip to content

Instantly share code, notes, and snippets.

@kixxauth
Created October 11, 2012 10:18
Show Gist options
  • Save kixxauth/3871453 to your computer and use it in GitHub Desktop.
Save kixxauth/3871453 to your computer and use it in GitHub Desktop.
Good Modal Dialogs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Sane Modal</title>
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="positioned.css">
</head><body>
<div id="base-layer">
<header>
<hgroup>
<h1>Broken Modal</h1>
<h2>There are problems with long dialogs that require scrolling.</h2>
</hgroup>
</header>
<article>
<section class="section-aa">
<h3>Call To Action</h3>
<p>Please, Please, Please click the button.</p>
<div>
<button class="open-dialog" href="#dialog">Open Dialog</button>
</div>
</section>
<section class="section-a">
<h3>Section 1</h3>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin et justo sit
amet felis accumsan auctor. Sed interdum ultricies nulla, et bibendum eros
pellentesque sed. Nullam tincidunt elit in odio vestibulum suscipit. Aliquam
sed lectus eget magna suscipit rutrum. Proin eu mauris sapien. Class aptent
taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
Sed quis mauris ac dolor mattis pulvinar eget a turpis. Fusce venenatis, erat
eget volutpat suscipit, arcu leo eleifend magna, faucibus feugiat urna est eu
mauris.
</p>
</section>
<section class="section-b">
<h3>Section 2</h3>
<p>
Nunc eu vehicula ligula. Sed porta vestibulum suscipit. Donec scelerisque
libero at tortor fermentum sit amet scelerisque eros auctor. Vestibulum
sollicitudin viverra nulla, sed luctus odio condimentum in. Proin feugiat
venenatis dictum. Fusce eget arcu dolor, in egestas turpis. Nullam mattis
semper ligula a porttitor.
</p>
</section>
<section class="section-c">
<h3>Section 3</h3>
<p>
Curabitur bibendum euismod nisl, id convallis lorem pellentesque sit
amet. Integer sed tortor vestibulum turpis feugiat gravida. Cras lacus
eros, varius sed egestas eget, eleifend at mi. Donec bibendum imperdiet
est, nec euismod massa pretium nec. Vestibulum ante ipsum primis in
faucibus orci luctus et ultrices posuere cubilia Curae; Praesent pulvinar
tincidunt mauris, ac viverra sem vehicula eget. In mollis condimentum
nisl, eget ultrices orci interdum quis. Aliquam non fermentum justo.
Morbi quis mattis metus. Mauris suscipit eleifend metus, eu cursus arcu
fermentum non. Vestibulum non libero condimentum felis gravida malesuada.
Curabitur egestas sem posuere est fringilla posuere.
</p>
</section>
<section class="section-d">
<h3>Another Call To Action</h3>
<div>
<button class="open-dialog" href="#dialog">Open Dialog</button>
</div>
</section>
</article>
<footer>
<h4>Footer</h4>
<p>
Web Development by
<a href="http://www.kixx.name"
title="This website is built by Kris Walker.">Kris Walker</a>.
</p>
</footer>
<!--
Markup for the modal dialog is all inside this container, which will be
positioned absolutely by CSS in the external stylesheet. The default state is
"display:none;" which is set inline here.
-->
<div id="dialog" style="display:none;">
<form id="dialog-form" action="/foo/bar" method="POST">
<h3 class="header">Subscribe</h3>
<p>
<label for="id_email">Email:</label>
<input name="email" id="id_email" type="email" size="24" placeholder="[email protected]" />
</p>
<p>
<label for="id_zip">Zip:</label>
<input name="zip" id="id_zip" type="text" size="8" placeholder="zip code" />
</p>
<p>
<label>Another Question:</label><br />
<textarea rows="8"></textarea>
</p>
<p>
<label>Another Question:</label><br />
<textarea rows="8"></textarea>
</p>
<p>
<label>Another Question:</label><br />
<textarea rows="8"></textarea>
</p>
<p>
<label>Another Question:</label><br />
<textarea rows="8"></textarea>
</p>
<p>
<label>Another Question:</label><br />
<textarea rows="8"></textarea>
</p>
<p>
<label>Another Question:</label><br />
<textarea rows="8"></textarea>
</p>
<p>
<input type="submit" value="Subscribe" />
<input type="reset" value="Cancel" />
</p>
</form>
</div>
</div><!-- #base-layer -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="broken.js"></script>
</body></html>
var VIEW = (function () {
var VIEW = {}
, $dialog = $('#dialog')
VIEW.showDialog = function () {
var $w = $(window)
, thisScrollPos = $w.scrollTop()
, offset
// determine the offset from the top of the page to position the dialog
// (adjust these numbers for your UI by experimenting)
offset = 20; // default
// If this is bigger than a smartphone, then offset should be 10% of
// the viewport
if ($w.innerWidth() > 700) {
offset = $w.innerHeight() * .1;
}
topPosition = thisScrollPos + offset;
// position and show the dialog
$dialog.css('top', topPosition + 'px').show();
return false; // use this function as a click handler
};
VIEW.closeDialog = function () {
$dialog.hide();
return false; // use this function as a click handler
};
return VIEW;
}());
$('.open-dialog').click(VIEW.showDialog);
$('#dialog-form').on('submit reset', VIEW.closeDialog);
/* Base Styles */
body {
color: #0f1318;
}
article section {
margin: 0;
padding: 6em 1em 2em;
}
.section-a {
background: #e2e7ed;
}
.section-b {
background: #cdd6e0;
}
.section-c {
background: #b8c4d3;
}
.section-d {
background: #a3b3c6;
}
footer {
margin-top: 2em;
background: #0f1318;
padding: 2em;
}
footer, footer a {
color: #fff;
}
/* The base layer wrapper changes to a fixed width box if the device screen is
* wider than 768px
*/
@media (min-width: 768px) {
#base-layer {
width: 700px;
margin: 0 auto;
}
}
/* Dialog Styles */
#dialog {
width: 90%;
margin-left: 4%;
border: 1px solid #0f1318;
background: #ecf0f3;
}
#dialog-form {
margin: 1em;
}
#dialog textarea {
width: 90%;
}
@media (min-width: 680px) {
#dialog {
width: 600px;
margin-left: auto;
margin-right: auto;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Sane Modal</title>
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="deck.css">
</head><body>
<!-- All normal, non-dialog content goes in the base layer -->
<div id="base-layer">
<div id="content-card">
<header>
<hgroup>
<h1>The Card Deck With Smart Scroll Position</h1>
<h2>Your Users Will Love You</h2>
</hgroup>
</header>
<article>
<section class="section-aa">
<h3>Call To Action</h3>
<p>Please, Please, Please click the button.</p>
<div>
<button class="open-dialog" href="#dialog">Open Dialog</button>
</div>
</section>
<section class="section-a">
<h3>Section 1</h3>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin et justo sit
amet felis accumsan auctor. Sed interdum ultricies nulla, et bibendum eros
pellentesque sed. Nullam tincidunt elit in odio vestibulum suscipit. Aliquam
sed lectus eget magna suscipit rutrum. Proin eu mauris sapien. Class aptent
taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
Sed quis mauris ac dolor mattis pulvinar eget a turpis. Fusce venenatis, erat
eget volutpat suscipit, arcu leo eleifend magna, faucibus feugiat urna est eu
mauris.
</p>
</section>
<section class="section-b">
<h3>Section 2</h3>
<p>
Nunc eu vehicula ligula. Sed porta vestibulum suscipit. Donec scelerisque
libero at tortor fermentum sit amet scelerisque eros auctor. Vestibulum
sollicitudin viverra nulla, sed luctus odio condimentum in. Proin feugiat
venenatis dictum. Fusce eget arcu dolor, in egestas turpis. Nullam mattis
semper ligula a porttitor.
</p>
</section>
<section class="section-c">
<h3>Section 3</h3>
<p>
Curabitur bibendum euismod nisl, id convallis lorem pellentesque sit
amet. Integer sed tortor vestibulum turpis feugiat gravida. Cras lacus
eros, varius sed egestas eget, eleifend at mi. Donec bibendum imperdiet
est, nec euismod massa pretium nec. Vestibulum ante ipsum primis in
faucibus orci luctus et ultrices posuere cubilia Curae; Praesent pulvinar
tincidunt mauris, ac viverra sem vehicula eget. In mollis condimentum
nisl, eget ultrices orci interdum quis. Aliquam non fermentum justo.
Morbi quis mattis metus. Mauris suscipit eleifend metus, eu cursus arcu
fermentum non. Vestibulum non libero condimentum felis gravida malesuada.
Curabitur egestas sem posuere est fringilla posuere.
</p>
</section>
<section class="section-d">
<h3>Another Call To Action</h3>
<div>
<button class="open-dialog" href="#dialog">Open Dialog</button>
</div>
</section>
</article>
<footer>
<h4>Footer</h4>
<p>
Web Development by
<a href="http://www.kixx.name"
title="This website is built by Kris Walker.">Kris Walker</a>.
</p>
</footer>
</div><!-- #content-card -->
<!--
Markup for the modal dialog is all inside this card container. The default
state is "display:none;" which is set inline here.
-->
<div id="dialog" style="display:none;">
<form id="dialog-form" action="/foo/bar" method="POST">
<h3 class="header">Subscribe</h3>
<p>
<label for="id_email">Email:</label>
<input name="email" id="id_email" type="email" size="24" placeholder="[email protected]" />
</p>
<p>
<label for="id_zip">Zip:</label>
<input name="zip" id="id_zip" type="text" size="8" placeholder="zip code" />
</p>
<p>
<label>Another Question:</label><br />
<textarea rows="8"></textarea>
</p>
<p>
<label>Another Question:</label><br />
<textarea rows="8"></textarea>
</p>
<p>
<label>Another Question:</label><br />
<textarea rows="8"></textarea>
</p>
<p>
<label>Another Question:</label><br />
<textarea rows="8"></textarea>
</p>
<p>
<label>Another Question:</label><br />
<textarea rows="8"></textarea>
</p>
<p>
<label>Another Question:</label><br />
<textarea rows="8"></textarea>
</p>
<p>
<input type="submit" value="Subscribe" />
<input type="reset" value="Cancel" />
</p>
</form>
</div><!-- #dialog -->
</div><!-- #base-layer -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="deck.js"></script>
</body></html>
var VIEW = (function () {
var VIEW = {}
, $dialogCard = $('#dialog')
, $contentCard = $('#content-card')
, scrollPos = null
VIEW.showDialog = function () {
var $w = $(window)
, thisScrollPos = $w.scrollTop()
, offset
// Cache the current scroll position of the base layer. This is the
// last place where the user has seen the base layer, and we want to
// be able to put them right back there after the modal dialog
// closes.
if (scrollPos === null) scrollPos = thisScrollPos;
// hide the main content
$contentCard.hide();
// Put the dialog into the layout flow, but don't reveal it to the user
// until we have scrolled into position.
$dialogCard.css('visibility', 'hidden').show();
$w.scrollTop(0);
$dialogCard.css('visibility', 'visible');
return false; // use this function as a click handler
};
VIEW.closeDialog = function () {
$dialogCard.hide(); // hide the dialog
// Put the content card back into the layout flow, but it will not be visible
// to the user, since 'visiblity: hidden;' is set.
$contentCard.css('visibility', 'hidden').show();
// Return the base view back to the scroll position where the user last
// saw it. This way, they don't get lost on your UI.
$(window).scrollTop(scrollPos);
scrollPos = null; // Reset for the next dialog open event
// then show the content card to the user again
$contentCard.css('visibility', 'visible');
return false; // use this function as a click handler
};
return VIEW;
}());
$('.open-dialog').click(VIEW.showDialog);
$('#dialog-form').on('submit reset', VIEW.closeDialog);
/* Base Styles */
body {
color: #0f1318;
}
article section {
margin: 0;
padding: 6em 1em 2em;
}
.section-a {
background: #e2e7ed;
}
.section-b {
background: #cdd6e0;
}
.section-c {
background: #b8c4d3;
}
.section-d {
background: #a3b3c6;
}
footer {
margin-top: 2em;
background: #0f1318;
padding: 2em;
}
footer, footer a {
color: #fff;
}
/* The base layer wrapper changes to a fixed width box if the device screen is
* wider than 768px
*/
@media (min-width: 768px) {
#base-layer {
width: 700px;
margin: 0 auto;
}
}
/* Dialog Styles */
#dialog {
position: absolute;
width: 90%;
margin-left: 4%;
border: 1px solid #0f1318;
background: #ecf0f3;
}
#dialog-form {
margin: 1em;
}
#dialog textarea {
width: 90%;
}
@media (min-width: 680px) {
#dialog {
width: 600px;
left: 50%;
margin-left: -300px;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Sane Modal</title>
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="positioned.css">
</head><body>
<!-- All normal, non-dialog content goes in the base layer -->
<div id="base-layer">
<header>
<hgroup>
<h1>Hiding The Background Caching the Scroll Position</h1>
<h2>This is an improvement, but not great.</h2>
</hgroup>
</header>
<article>
<section class="section-aa">
<h3>Call To Action</h3>
<p>Please, Please, Please click the button.</p>
<div>
<button class="open-dialog" href="#dialog">Open Dialog</button>
</div>
</section>
<section class="section-a">
<h3>Section 1</h3>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin et justo sit
amet felis accumsan auctor. Sed interdum ultricies nulla, et bibendum eros
pellentesque sed. Nullam tincidunt elit in odio vestibulum suscipit. Aliquam
sed lectus eget magna suscipit rutrum. Proin eu mauris sapien. Class aptent
taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
Sed quis mauris ac dolor mattis pulvinar eget a turpis. Fusce venenatis, erat
eget volutpat suscipit, arcu leo eleifend magna, faucibus feugiat urna est eu
mauris.
</p>
</section>
<section class="section-b">
<h3>Section 2</h3>
<p>
Nunc eu vehicula ligula. Sed porta vestibulum suscipit. Donec scelerisque
libero at tortor fermentum sit amet scelerisque eros auctor. Vestibulum
sollicitudin viverra nulla, sed luctus odio condimentum in. Proin feugiat
venenatis dictum. Fusce eget arcu dolor, in egestas turpis. Nullam mattis
semper ligula a porttitor.
</p>
</section>
<section class="section-c">
<h3>Section 3</h3>
<p>
Curabitur bibendum euismod nisl, id convallis lorem pellentesque sit
amet. Integer sed tortor vestibulum turpis feugiat gravida. Cras lacus
eros, varius sed egestas eget, eleifend at mi. Donec bibendum imperdiet
est, nec euismod massa pretium nec. Vestibulum ante ipsum primis in
faucibus orci luctus et ultrices posuere cubilia Curae; Praesent pulvinar
tincidunt mauris, ac viverra sem vehicula eget. In mollis condimentum
nisl, eget ultrices orci interdum quis. Aliquam non fermentum justo.
Morbi quis mattis metus. Mauris suscipit eleifend metus, eu cursus arcu
fermentum non. Vestibulum non libero condimentum felis gravida malesuada.
Curabitur egestas sem posuere est fringilla posuere.
</p>
</section>
<section class="section-d">
<h3>Another Call To Action</h3>
<div>
<button class="open-dialog" href="#dialog">Open Dialog</button>
</div>
</section>
</article>
<footer>
<h4>Footer</h4>
<p>
Web Development by
<a href="http://www.kixx.name"
title="This website is built by Kris Walker.">Kris Walker</a>.
</p>
</footer>
</div><!-- #base-layer -->
<!--
Markup for the modal dialog is all inside this container, which will be
positioned absolutely by CSS in the external stylesheet. The default state is
"display:none;" which is set inline here.
-->
<div id="dialog" style="display:none;">
<form id="dialog-form" action="/foo/bar" method="POST">
<h3 class="header">Subscribe</h3>
<p>
<label for="id_email">Email:</label>
<input name="email" id="id_email" type="email" size="24" placeholder="[email protected]" />
</p>
<p>
<label for="id_zip">Zip:</label>
<input name="zip" id="id_zip" type="text" size="8" placeholder="zip code" />
</p>
<p>
<label>Another Question:</label><br />
<textarea rows="8"></textarea>
</p>
<p>
<label>Another Question:</label><br />
<textarea rows="8"></textarea>
</p>
<p>
<label>Another Question:</label><br />
<textarea rows="8"></textarea>
</p>
<p>
<label>Another Question:</label><br />
<textarea rows="8"></textarea>
</p>
<p>
<label>Another Question:</label><br />
<textarea rows="8"></textarea>
</p>
<p>
<label>Another Question:</label><br />
<textarea rows="8"></textarea>
</p>
<p>
<input type="submit" value="Subscribe" />
<input type="reset" value="Cancel" />
</p>
</form>
</div><!-- #dialog -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="positioned.js"></script>
</body></html>
var VIEW = (function () {
var VIEW = {}
, $dialog = $('#dialog')
, $base = $('#base-layer')
, scrollPos = null
function hideBaseLayer() {
// by using visibility:hidden; we are able to maintain scroll position
$base.css('visibility', 'hidden');
}
function showBaseLayer() {
// Return the base view back to the scroll position where the user last
// saw it. This way, they don't get lost on your UI.
$(window).scrollTop(scrollPos);
scrollPos = null; // Reset for the next dialog open event
$base.css('visibility', 'visible'); // then show the base layer again
}
VIEW.showDialog = function () {
var $w = $(window)
, thisScrollPos = $w.scrollTop()
, offset
// Cache the current scroll position of the base layer. This is the
// last place where the user has seen the base layer, and we want to
// be able to put them right back there after the modal dialog
// closes.
if (scrollPos === null) scrollPos = thisScrollPos;
hideBaseLayer(); // Make the base layer invisible
// determine the offset from the top of the page to position the dialog
// at. (adjust these numbers for your UI by experimenting)
offset = 20 // default
// If this is bigger than a smartphone, then offset should be 10% of
// the viewport
if ($w.innerWidth() > 700) {
offset = $w.innerHeight() * .1;
}
topPosition = thisScrollPos + offset;
// position and show the dialog
$dialog.css('top', topPosition + 'px').show();
return false; // use this function as a click handler
};
VIEW.closeDialog = function () {
$dialog.hide();
showBaseLayer();
return false; // use this function as a click handler
};
return VIEW;
}());
$('.open-dialog').click(VIEW.showDialog);
$('#dialog-form').on('submit reset', VIEW.closeDialog);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment