octocatstartv

Pen Settings

CSS Base

Vendor Prefixing

Add External CSS

These stylesheets will be added in this order and before the code you write in the CSS editor. You can also add another Pen here, and it will pull the CSS from it. Try typing "font" or "ribbon" below.

Quick-add: + add another resource
via CSS Lint

Add External JavaScript

These scripts will run in this order and before the code in the JavaScript editor. You can also link to another Pen here, and it will run the JavaScript from it. Also try typing the name of any popular library.

Quick-add: + add another resource
via JS Hint

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Want a Run Button?

If active, the preview will update automatically when you change code.

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 -->
            
          
!

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;
}
            
          
!

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();

}());
            
          
!
999px
Close

Asset uploading is a PRO feature.

As a PRO member, you can drag-and-drop upload files here to use as resources. Images, Libraries, JSON data... anything you want. You can even edit them anytime, like any other code on CodePen.

Go PRO

Loading ..................

Console