{"id":7663,"date":"2010-10-08T05:49:31","date_gmt":"2010-10-08T12:49:31","guid":{"rendered":"http:\/\/css-tricks.com\/?p=7663"},"modified":"2010-12-08T18:03:17","modified_gmt":"2010-12-09T01:03:17","slug":"make-a-view-source-button","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/make-a-view-source-button\/","title":{"rendered":"Make a \u2018View Source\u2019\u00a0Button"},"content":{"rendered":"

Remy Sharp’s cool site for HTML5 demos<\/a> has a “View Source” button on each of the individual demo<\/a> pages. Click it, and you see the entire source code for the page you are looking at. It’s not a popup or a new tab, it just shows the source right there on the page. I thought that was cool so I set out to recreate it my own way.<\/p>\n

<\/p>\n

The Fancy Way<\/h3>\n

\"\"<\/p>\n

View Demo<\/a>   Download Files<\/a><\/p>\n

The idea that first came to mind was to use CSS3’s :target pseudo class. We talked about these a while back while exporting CSS3 tabs<\/a>. The idea is to have a link that links to the #source-code hash tag. That puts #source-code at the end of the URL, and allows the :target selector to match and apply styling. The basics:<\/p>\n

<a href=\"#source-code\">View Source<\/a><\/code><\/pre>\n
#source-code { display: none; }\r\n#source-code:target { display: block; }<\/code><\/pre>\n

Now it’s just a matter of filling that #source-code element with the actual source code. That is JavaScript territory, since we’ll want to pull that code dynamically, not actually repeat the source code just for sake of display. This way whenever we alter the page, we don’t have to adjust anything, our View Source button shows the updated source code as written. We can lean on jQuery to grab this for us:<\/p>\n

var html = $(\"html\").html();<\/code><\/pre>\n

That will give us all the HTML for the page, excluding only the DOCTYPE and actual <html><\/code> tags. You could also access this through document.documentElement.innerHTML<\/code> but hey, if we’re gonna use jQuery let’s just use it. We’ll create a brand new <pre><\/code> element, and fill it with a “fake” DOCTYPE and html tags, and then drop in that HTML that we just gathered from the page. <\/p>\n

A few quick considerations with that gathered HTML: 1) Let’s escape the <‘s so they show on the page correctly without trying to render as actual HTML. 2) Let’s “linkify” any links it finds so they can be clickable (taken in part from here<\/a>). <\/p>\n

Then we’ll place that newly created <pre><\/code> inside our #source-code div.<\/p>\n

$(function() {\r\n\t$(\"<pre \/>\", {\r\n\t\t\"html\":   '&lt;!DOCTYPE html>\\n&lt;html>\\n' + \r\n\t\t\t\t$(\"html\")\r\n\t\t\t\t\t.html()\r\n\t\t\t\t\t.replace(\/[<>]\/g, function(m) { return {'<':'&lt;','>':'&gt;'}[m]})\r\n\t\t\t\t\t.replace(\/((ftp|http|https):\\\/\\\/(\\w+:{0,1}\\w*@)?(\\S+)(:[0-9]+)?(\\\/|\\\/([\\w#!:.?+=&%@!\\-\\\/]))?)\/gi,'<a href=\"$1\">$1<\/a>') + \r\n\t\t\t\t'\\n&lt;\/html>'\r\n\t}).appendTo(\"#source-code\");\r\n});<\/code><\/pre>\n

Closing the source code is deliciously simple. Since the “View Source” button, when clicked, adds a hash-tag to the page, that means that the “Back” button in the browser will work. Pressing it removes the hash tag, thus #source-code is no longer :targeted, and it is automatically hidden by CSS. The #source-code div also has within it a simple “X” graphic, which links to the hash tag “#”, which has the exact same effect.<\/p>\n

\n\"\"<\/p>\n

This just links to “#”, which takes our source viewing element out of :target, which instantly hides it.<\/p>\n<\/div>\n

Styling the code<\/h4>\n

One of the easiest ways to apply syntax highlighting to code is the google-code-prettify project<\/a>. This is a JavaScript based highlighter. Essentially you link up the JavaScript file and the CSS file that comes with the download:<\/p>\n

<link rel='stylesheet' href='css\/prettify.css' \/>\r\n<script src=\"prettify\/prettify.js\"><\/script><\/code><\/pre>\n

Make sure your <pre><\/code> tags have a class of “prettyprint”. I added this to the element creation syntax we’re using to create the <pre><\/code>:<\/p>\n

\"class\": \"prettyprint\"<\/code><\/pre>\n

and call the prettyPrint()<\/code> function when the page is ready. They recommend adding an onload to the body tag, but since we’re using the jQuery DOM ready function, we can just call it inside there.<\/p>\n

I altered the colors a bit to work on the dark background. This is the top part of the prettify.css file:<\/p>\n

.str { color: #61ff74; }\r\n.kwd { color: #ea5eff; }\r\n.com { color: white; }\r\n.typ { color: red; }\r\n.lit { color: red; }\r\n.pun { color: white; }\r\n.pln { color: white; }\r\n.tag { color: #ffab58; }\r\n.atn { color: #fabb4e; }\r\n.atv { color: #ffd996; }\r\n.dec { color: red; }<\/code><\/pre>\n
\n\"\"<\/p>\n

Prettify works by wrapping parts of the code in spans, which colorize that part. Also notice the properly escaped characters.<\/p>\n<\/div>\n

Compatibility<\/h4>\n

Pretty much everything modern, as long as it supports CSS3 :target. Breaks in IE 8 and down, but OK in IE 9.<\/p>\n

Primitive Method<\/h3>\n

Firefox and Chrome support the URL protocol “view-source:” which opens up its native source code viewer. If you are making these links only for yourself, you could do:<\/p>\n

<a class=\"button\" onClick='window.location=\"view-source:\" + window.location.href'>View Source<\/a><\/code><\/pre>\n

Remy’s Method<\/h3>\n

Turns out the way I did it is pretty much nothing like how it’s done on the HTML5 demos pages<\/a>. This is my attempt at explaining his technique:<\/p>\n

    \n
  1. Watch for all clicks<\/li>\n
  2. If click was on a link with hash-tag #view-source…<\/li>\n
  3. Add a class name of .view-source to the body (this class reveals the otherwise hidden <pre><\/code>)<\/li>\n
  4. If there isn’t one already, append a <pre><\/code> to the body with ID #view-source<\/li>\n
  5. \n<pre><\/code> is filled with HTML taken from document.documentElement.innerHTML<\/code><\/li>\n
  6. Set a timer to check ever 200 milliseconds if the URL still has the hash tag #view-source on it<\/li>\n
  7. If the hash tag is gone or has changed, remove the class name from the body (hiding the <pre><\/code>) and cancel the timer.<\/li>\n<\/ol>\n
    (function () {\r\n\r\nvar pre = document.createElement('pre');\r\npre.id = \"view-source\"\r\n\r\n\/\/ private scope to avoid conflicts with demos\r\naddEvent(window, 'click', function (event) {\r\n  if (event.target.hash == '#view-source') {\r\n    \/\/ event.preventDefault();\r\n    if (!document.getElementById('view-source')) {\r\n      pre.innerHTML = ('<!DOCTYPE html>\\n<html>\\n' + document.documentElement.innerHTML + '\\n<\/html>').replace(\/[<>]\/g, function (m) { return {'<':'&lt;','>':'&gt;'}[m]});\r\n      document.body.appendChild(pre);      \r\n    }\r\n    document.body.className = 'view-source';\r\n    \r\n    var sourceTimer = setInterval(function () {\r\n      if (window.location.hash != '#view-source') {\r\n        clearInterval(sourceTimer);\r\n        document.body.className = '';\r\n      }\r\n    }, 200);\r\n  }\r\n});\r\n  \r\n})();<\/code><\/pre>\n

    And then the clever bit of CSS which allows the source code to be hidden normally and take over the screen when the body acquires the .view-source<\/code> class:<\/p>\n

    #view-source {\r\n  display: none;\r\n}\r\n\r\nbody.view-source > * {\r\n  display: none;\r\n}\r\n\r\nbody.view-source #view-source {\r\n  display: block !important;\r\n}<\/code><\/pre>\n

    Big time benefits<\/h4>\n

    The cool part about Remy’s method is that it’s far more cross-browser compatible than my idea. It uses pure JavaScript, so it’s not library dependent at all, and it doesn’t use any technique that is “fancy” and wouldn’t work in older(ish) browsers. He could have used the hashchange<\/code> event, for example, to watch for when the #view-source goes away, but that’s not supported in some older browsers so even that was handled well.<\/p>\n

    Usage<\/h3>\n

    Of course, feel free to use this however you would like.<\/p>\n

    View Demo<\/a>   Download Files<\/a><\/p>\n

    In its current form, this code relies on certain markup on the page, namely the button itself and the #view-source div. This could definitely be converted into a plugin which appends those things automatically which would make this much easier to implement. I may do that eventually.<\/p>\n

    This also has me thinking that 1) I really need to get on putting in syntax highlighting back into the blog design itself here on CSS-Tricks and 2) I should do this View Source thing on all demo pages. Both of those things will get done in time.<\/p>\n

    Another thing that would be cool is to bookmarkletize this. That would be especially cool for Safari which has crappy View Source (although there is an extension<\/a> to fix that). Still, none of the browsers do view source in a modal window, which I think is sometimes nicer than opening a new window or tab.<\/p>\n","protected":false},"excerpt":{"rendered":"

    Remy Sharp’s cool site for HTML5 demos has a “View Source” button on each of the individual demo pages. Click it, and you see the entire source code for the page you are looking at. It’s not a popup or a new tab, it just shows the source right there on the page. I thought […]<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"c2c_always_allow_admin_comments":false,"footnotes":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":[]},"categories":[19,4],"tags":[],"jetpack_publicize_connections":[],"acf":[],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":8030,"url":"https:\/\/css-tricks.com\/text-fade-read-more\/","url_meta":{"origin":7663,"position":0},"title":"Text Fade Out \/ Read More Link","date":"December 7, 2010","format":false,"excerpt":"A section of text that fades into the nothingness. But wait, a beacon. A \"read more\" link shines through the darkness. Click upon it and all text is revealed! CSS3 gradients are used for the text fading and jQuery is used to handle the animated reveal.","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2010\/12\/fadeoutreadmore.png?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":6478,"url":"https:\/\/css-tricks.com\/css3-button-maker\/","url_meta":{"origin":7663,"position":1},"title":"CSS3 Button Maker","date":"May 30, 2010","format":false,"excerpt":"Drag things, pick colors, make a nice class for your buttons... introducing the Button Maker. Boy, that's a nice button right there. I'm saying \"CSS3\", because these make use of gradients, shadows, and rounded corners which contribute greatly to their button-ness. In older browers that don't support these properties, the\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/csstricks-uploads\/buttonmakerexample.jpg?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":303217,"url":"https:\/\/css-tricks.com\/css4-is-a-bad-idea\/","url_meta":{"origin":7663,"position":2},"title":"CSS4 is a Bad Idea","date":"February 5, 2020","format":false,"excerpt":"Louis Lazaris, reacting to the idea of CSS4: The reason \u201cCSS3\u201d worked is because it was real. It was the successor to \u201cCSS2.1\u201d. Everything after CSS2.1 was considered to be under the umbrella of \u201cCSS3\u201d. The gist is that CSS4 isn't real, so won't work, and we don't need it\u2026","rel":"","context":"In "Link"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":6443,"url":"https:\/\/css-tricks.com\/parallax-background-css3\/","url_meta":{"origin":7663,"position":3},"title":"Parallax Background of Stars (plus CSS3 keyframe animation)","date":"May 25, 2010","format":false,"excerpt":"This idea was originally published on 3\/12\/2008, where you had to resize the browser window to see the parallax. I updated it on 4\/6\/2009 to utilize jQuery to move the stars automatically. I'm now updating it again to use CSS3 to animate the stars. There is some debate if these\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/csstricks-uploads\/css3starsmoving.jpg?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":6522,"url":"https:\/\/css-tricks.com\/ajax-image-uploading\/","url_meta":{"origin":7663,"position":4},"title":"Ajax Image Uploading (With Less Suck)","date":"June 10, 2010","format":false,"excerpt":"This technology demo is courtesy of ZURB and the post was co-authored by ZURB and Chris. How do you upload images now? You select a file and click upload. Simple right? Except once you select your image you can no longer see what was selected. The name of the file\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":6180,"url":"https:\/\/css-tricks.com\/loading-dots-plugin\/","url_meta":{"origin":7663,"position":5},"title":"Loading Dots jQuery Plugin","date":"April 16, 2010","format":false,"excerpt":"Loading.... It's a design pattern we've all seen before, because it's just good user feedback. This is a quick jQuery plugin to apply it to any element when called (exactly in the middle of it).","rel":"","context":"In "Article"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"featured_media_src_url":null,"_links":{"self":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/7663"}],"collection":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/comments?post=7663"}],"version-history":[{"count":14,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/7663\/revisions"}],"predecessor-version":[{"id":8083,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/7663\/revisions\/8083"}],"wp:attachment":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media?parent=7663"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/categories?post=7663"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/tags?post=7663"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}