CodePen

HTML

            
              <div class="demo">
  
  <h1>QuestionMark.js</h1>

<p>A lightweight, cross-browser, modal window that displays shortcut keys for your app, <a href="https://plus.google.com/u/0/118100898483063383963/posts/V12mRNmsiWg">similar to Twitter, Gmail, GitHub, etc.</a></p>

<p>This is an example page.</p>

<p>Press the <code>?</code> key to see the modal.</p>

<p>More info:<br><a href="http://impressivewebs.github.io/QuestionMark.js/">http://impressivewebs.github.io/QuestionMark.js/</a></p>

<p>© <a href="https://twitter.com/ImpressiveWebs">Louis Lazaris</a></p>
</div><!-- .demo -->
            
          
!

↑ Insert the most common viewport meta tag

CSS

            
              /* the first section below is just
for the demo page */

.demo {
    word-wrap: break-word;
    text-align: center;
    font-family: Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
}

.demo h1 {
    color: #aaa;
    font-size: 3em;
}

.demo p {
    font-family: Arial, sans-serif;
    max-width: 880px;
    color: #555;
    margin: 0 auto 30px auto;
    font-size: 30px;
}

.demo p:last-child {
    font-size: 20px;
}

.demo a {
    color: firebrick;
}

.demo a:hover {
    color: blue;
}

.demo code {
    background: #888;
    color: white;
    padding: 3px 8px;
}

/* question.mark.css starts here */

.help-underlay * {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
}

.help-underlay {
  position: absolute;
  background: #555;
  background: rgba(0,0,0,0.5);
  opacity: 0;
  -webkit-transition: opacity .2s linear;
  -moz-transition: opacity .2s linear;
  -o-transition: opacity .2s linear;
  transition: opacity .2s linear;
  z-index: -1;
  height: 0;
  left: 0; right: 0; top: 0; bottom: 0;
}

.ie8 .help-underlay {
  -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(opacity=95)"; /* for IE8 in IE7 mode */
  filter: alpha(opacity=95); /* for IE8 */
  z-index: 8888;
  height: 0;
}

.help-modal {
  position: absolute;
  z-index: 99999;
  left: 0; right: 0; top: 0; bottom: 0;
  width: 80%;
  margin: auto;
  background: #fff;
  color: #676767;
  max-height: 80%;
  overflow: auto;
  font-family: Arial, sans-serif;
  -webkit-border-radius: 3px;
  -moz-border-radius: 3px;
  border-radius: 3px;
  -webkit-box-shadow: 0 4px 12px rgba(0,0,0,.4), inset 0 1px 0 rgba(255,255,255,.5);
  box-shadow: 0 4px 12px rgba(0,0,0,.4), inset 0 1px 0 rgba(255,255,255,.5);
  -webkit-transition: width .2s linear;
  -moz-transition: width .2s linear;
  -o-transition: width .2s linear;
  transition: width .2s linear;
}

  .help-modal-content {
    padding: 0 20px;
  }

  .help-close {
    position: absolute;
    top: .4em;
    right: .5em;
    font-size: 1.8em;
    cursor: pointer;
    -webkit-transition: color .2s linear;
    -moz-transition: color .2s linear;
    -o-transition: color .2s linear;
    transition: color .2s linear;
  }

    .help-close:hover {
      color: #000;
    }

.help-modal h1 {
  text-align: center;
  margin: .5em;
  font-size: 1.5em;
  padding-bottom: .4em;
  border-bottom: solid 2px #ccc;
  font-weight: normal;
}

  .help-modal h1 .help-key {
    float: none;
    line-height: 1.5;
    position: relative;
    bottom: 4px;
  }

.help-isVisible {
  opacity: 1;
  height: auto;
  z-index: 8888;
}

.ie8 .help-underlay.help-isVisible {
  visibility: visible\9;
  height: auto;
}

.help-list-wrap {
  overflow: hidden;
  margin: 0 auto;
  -webkit-transition: width .2s linear;
  -moz-transition: width .2s linear;
  -o-transition: width .2s linear;
  transition: width .2s linear;
}

.help-list {
  list-style: none;
  margin: 0;
  padding: 0 0 10px 0;
  overflow: hidden;
  float: left;
  width: 280px;
}

  .help-list li {
    margin-right: 40px;
  }

.help-key-unit {
  line-height: 1.8;
  margin-right: 2em;
  padding: 5px 0;
}

.help-key {
  min-width: 60px;
  float: left;
  clear: left;
  position: relative;
  bottom: 2px;
}

.help-key span {
  font-size: 14px;
  color: #555;
  display: inline-block;
  padding: 0 8px;
  text-align: center;
  background-color: #eee;
  background-repeat: repeat-x;
  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#eee));
  background-image: -webkit-linear-gradient(#f5f5f5 0%, #eee 100%);
  background-image: -moz-linear-gradient(#f5f5f5 0%, #eee 100%);
  background-image: -o-linear-gradient(#f5f5f5 0%, #eee 100%);
  background-image: linear-gradient(#f5f5f5 0%, #eee 100%);
  border: 1px solid #ccc;
  -webkit-border-radius: 3px;
  -moz-border-radius: 3px;
  border-radius: 3px;
  -webkit-box-shadow: inset 0 1px 0 #fff, 0 1px 0 #ccc;
  box-shadow: inset 0 1px 0 #fff, 0 1px 0 #ccc;
}

.help-key-def {
  display: inline-block;
  margin-left: 1em;
}
            
          
!
? ?
? ?
Must be a valid URL.
+ add another resource
via CSS Lint

JS

            
              /*
    QuestionMark.js by Louis Lazaris
    http://impressivewebs.github.io/QuestionMark.js/
    Use it for whatever you want, no credit needed.
    This script should work everywhere, including IE8+.
    If you want IE8 support, include the following 
    polyfill for addEventListener() at the top:
    https://gist.github.com/jonathantneal/2415137
    (included in the repo as attachevent.js).
    Doesn't work in IE6/7, but feel free to fork and fix.
*/

(function () {

    'use strict';

    function removeModal(helpUnderlay) {
        helpUnderlay.className = helpUnderlay.className.replace(/help-isVisible*/g, '');
        helpUnderlay.className = helpUnderlay.className.trim();
    }

    function getWindowWidth() {
        var w = window,
            d = document,
            e = d.documentElement,
            g = d.getElementsByTagName('body')[0],
            x = w.innerWidth || e.clientWidth || g.clientWidth;
            //y = w.innerHeight || e.clientHeight || g.clientHeight;
        return x;
    }

    function doModalSize(o) {
        // Find out how many columns there are, create array of heights
        o.helpColsTotal = 0;
        for (o.i = 0; o.i < o.helpLists.length; o.i += 1) {
            if (o.helpLists[o.i].className.indexOf('help-list') !== -1) {
                o.helpColsTotal += 1;
            }
            o.helpListsHeights[o.i] = o.helpLists[o.i].offsetHeight;
        }

        // get the tallest column from the array of heights
        o.maxHeight = Math.max.apply(Math, o.helpListsHeights);

        // Quasi-responsive
        if (getWindowWidth() <= 1180 && getWindowWidth() > 630 && o.helpColsTotal > 2) {
            o.helpColsTotal = 2;
            o.maxHeight = o.maxHeight * o.helpColsTotal;
        }

        if (getWindowWidth() <= 630) {
            o.maxHeight = o.maxHeight * o.helpColsTotal;
            o.helpColsTotal = 1;
        }

        // Change the width/height of the modal and wrapper to fit the columns
        // Sorry for the magic numbers. Whatevs.
        o.helpListWrap.style.offsetWidth = (o.helpList.offsetWidth * o.helpColsTotal) + 'px';
        o.helpListWrap.style.height = o.maxHeight + 'px';
        o.helpModal.style.width = (o.helpList.offsetWidth * o.helpColsTotal) + 60 + 'px';
        o.helpModal.style.height = o.maxHeight + 100 + 'px';
    }

    function doWhichKey(e) {
        e = e || window.event;
        var charCode = e.keyCode || e.which;
        //Line below not needed, but you can read the key with it
        //var charStr = String.fromCharCode(charCode);
        return charCode;
    }

    // Primary function, called in checkServerResponse()
    function doQuestionMark() {

        var helpUnderlay = document.getElementById('helpUnderlay'),
            helpModal = document.getElementById('helpModal'),
            helpClose = document.getElementById('helpClose'),
            timeOut = null,
            objDoSize = {
                i: null,
                maxHeight: null,
                helpListWrap: document.getElementById('helpListWrap'),
                helpList: document.querySelector('.help-list'),
                helpLists: document.querySelectorAll('.help-list'),
                helpModal: helpModal,
                helpColsTotal: null,
                helpListsHeights: []
            },
            classCol;

        doModalSize(objDoSize);

        document.addEventListener('keypress', function (e) {

            // 63 = '?' key
            // '?' key toggles the modal
            if (doWhichKey(e) === 63) {
                classCol = document.getElementById('helpUnderlay').className;
                if (classCol.indexOf('help-isVisible') === -1) {
                    document.getElementById('helpUnderlay').className += ' help-isVisible';
                }

            }

        }, false);

        document.addEventListener('keyup', function (e) {
            // 27 = ESC key
            if (doWhichKey(e) === 27) {
                removeModal(helpUnderlay);
            }
        }, false);

        // Modal is removed if the background is clicked
        helpUnderlay.addEventListener('click', function () {
            removeModal(helpUnderlay);
        }, false);

        // this prevents click on modal from removing the modal
        helpModal.addEventListener('click', function (e) {
            e.stopPropagation();
        }, false);

        // the close button
        helpClose.addEventListener('click', function () {
            removeModal(helpUnderlay);
        }, false);

        // If the window is resized, the doModalSize() function is called again.
        // If your menu includes only a single column of keyboard shortcuts,
        // then you won't need this. Keep only if you have 2 columns or more.
        window.onresize = function () {
            if (timeOut !== null) {
                clearTimeout(timeOut);
            }
            timeOut = setTimeout(function () {
                doModalSize(objDoSize);
            }, 100);
        };

    }

    // All the Ajax stuff is below.
    // Probably no reason to touch this unless you can optimize it.
    function getXhrObject() {
        var xhrObject = false;
        // All browsers (except IE6) use the 3 lines below
        if (window.XMLHttpRequest) {
            xhrObject = new XMLHttpRequest();
        }
        // If you need IE6 support, uncomment the following else/if:
        /*else if (window.ActiveXObject) {
            try {
                    xhrObject = new ActiveXObject("Msxml2.XMLHTTP");
                } catch(err) {
                    try {
                        xhrObject = new ActiveXObject("Microsoft.XMLHTTP");
                    } catch(err) {
                        xhrObject = false;
                    }
            }
        }*/
        return xhrObject;
    }

    function insertHelp(respText, callback) {
        // Opera kept inserting the content multiple times
        // so I added a check to insert it just once... bug??
        if (!document.getElementById('helpUnderlay')) {
            document.getElementsByTagName('body')[0].innerHTML += respText;
            callback();
        }
    }

    function checkServerResponse(ajaxCapable) {
        if (ajaxCapable.readyState === 4) {
            if (ajaxCapable.status === 200 || ajaxCapable.status === 304) {
                var respText = ajaxCapable.responseText;
                // here's where the help modal is inserted
                insertHelp(respText, function () {
                    doQuestionMark();
                });
            }
        }
    }

    function doAjax() {
        var ajaxCapable = getXhrObject();
        if (ajaxCapable) {
            ajaxCapable.onreadystatechange = function () {
                checkServerResponse(ajaxCapable);
            };
            ajaxCapable.open("GET", "http://codepen.io/chriscoyier/pen/difoC.html", true);
            ajaxCapable.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            ajaxCapable.send(null);
        } else {
            // Browser does not support ajax
            document.getElementsByTagName('body')[0].innerHTML += 'Error: Your browser does not support Ajax';
        }
    }

    // This fires all the Ajax stuff, and, in turn,
    // the primary function for the modal.
    doAjax();

}());
            
          
!
Must be a valid URL.
+ add another resource
via JS Hint
Loading ..................