DevTools Snippets

A collection of helpful snippets to use inside of browser devtools

View Or Contribute On Github

Instructions: The snippets can be used in any browser console. Chrome provides a 'snippets' feature that can be used to manage the scripts, while Firefox has a 'scratchpad' feature that lets you run, edit, and save chunks of JavaScript.

View the devtools-snippets project page. For more information about using them in your favorite browser, check out the README. Here is an article about snippets detailing why you might use them and additional features that would be nice.

Have a snippet you'd like to share with the world? Contributing is easy, there is a guide explaining more.


allcolors.js #   (view raw)

Print out all colors from computed styles used in elements on the page. Uses styled console.log calls to visualize each color.

snippets/allcolors/allcolors

// allcolors.js
// https://github.com/bgrins/devtools-snippets
// Print out CSS colors used in elements on the page.

(function () {
  // Should include colors from elements that have a border color but have a zero width?
  var includeBorderColorsWithZeroWidth = false;

  var allColors = {};
  var props = ["background-color", "color", "border-top-color", "border-right-color", "border-bottom-color", "border-left-color"];
  var skipColors = {
    "rgb(0, 0, 0)": 1,
    "rgba(0, 0, 0, 0)": 1,
    "rgb(255, 255, 255)": 1
  };

  [].forEach.call(document.querySelectorAll("*"), function (node) {
    var nodeColors = {};
    props.forEach(function (prop) {
      var color = window.getComputedStyle(node, null).getPropertyValue(prop),
        thisIsABorderProperty = (prop.indexOf("border") != -1),
        notBorderZero = thisIsABorderProperty ? window.getComputedStyle(node, null).getPropertyValue(prop.replace("color", "width")) !== "0px" : true,
        colorConditionsMet;

      if (includeBorderColorsWithZeroWidth) {
        colorConditionsMet = color && !skipColors[color];
      } else {
        colorConditionsMet = color && !skipColors[color] && notBorderZero;
      }

      if (colorConditionsMet) {
        if (!allColors[color]) {
          allColors[color] = {
            count: 0,
            nodes: []
          };
        }

        if (!nodeColors[color]) {
          allColors[color].count++;
          allColors[color].nodes.push(node);
        }

        nodeColors[color] = true;
      }
    });
  });

  function rgbTextToRgbArray(rgbText) {
    return rgbText.replace(/\s/g, "").match(/\d+,\d+,\d+/)[0].split(",").map(function(num) {
      return parseInt(num, 10);
    });
  }

  function componentToHex(c) {
    var hex = c.toString(16);
    return hex.length == 1 ? "0" + hex : hex;
  }

  function rgbToHex(rgbArray) {
    var r = rgbArray[0],
      g = rgbArray[1],
      b = rgbArray[2];
    return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
  }

  var allColorsSorted = [];
  for (var i in allColors) {
    var rgbArray = rgbTextToRgbArray(i);
    var hexValue = rgbToHex(rgbArray);

    allColorsSorted.push({
      key: i,
      value: allColors[i],
      hexValue: hexValue
    });
  }

  allColorsSorted = allColorsSorted.sort(function (a, b) {
    return b.value.count - a.value.count;
  });

  var nameStyle = "font-weight:normal;";
  var countStyle = "font-weight:bold;";
  function colorStyle(color) {
    return "background:" + color + ";color:" + color + ";border:1px solid #333;";
  };

  console.group("Total colors used in elements on the page: " + window.location.href + " are " + allColorsSorted.length);
  allColorsSorted.forEach(function (c) {
    console.groupCollapsed("%c    %c " + c.key + " " + c.hexValue + " %c(" + c.value.count + " times)",
      colorStyle(c.key), nameStyle, countStyle);
    c.value.nodes.forEach(function (node) {
      console.log(node);
    });
    console.groupEnd();
  });
  console.groupEnd("All colors used in elements on the page");

})();

cachebuster.js #   (view raw)

Overwrite all link and (optionally) script tags by adding Date.now() at the end of href and src attributes, respectively. By default processing scripts is not performed, you should change the variable process_scripts to true to run these.

snippets/cachebuster/cachebuster

//Cache Buster
(function (){
  var rep = /.*\?.*/,
      links = document.getElementsByTagName('link'),
      scripts = document.getElementsByTagName('script'),
      process_scripts = false;
  for (var i=0;i<links.length;i++){
    var link = links[i],
        href = link.href;
    if(rep.test(href)){
      link.href = href+'&'+Date.now();
    }
    else{
      link.href = href+'?'+Date.now();
    }

  }
  if(process_scripts){
    for (var i=0;i<scripts.length;i++){
      var script = scripts[i],
          src = script.src;
      if(rep.test(src)){
        script.src = src+'&'+Date.now();
      }
      else{
        script.src = src+'?'+Date.now();
      }

    }
  }
})();

console-save.js #   (view raw)

A simple way to save objects as .json files from the console, includes a chrome extension along with a plain script.

Usage

console.save(data, [filename])

Data can be a string or just an object, objects are passed through json.stringify() before writing to file. Filename is optional, defaults to ‘console.json’.

Licence

MIT

console.save

(function(console){

    console.save = function(data, filename){

        if(!data) {
            console.error('Console.save: No data')
            return;
        }

        if(!filename) filename = 'console.json'

        if(typeof data === "object"){
            data = JSON.stringify(data, undefined, 4)
        }

        var blob = new Blob([data], {type: 'text/json'}),
            e    = document.createEvent('MouseEvents'),
            a    = document.createElement('a')

        a.download = filename
        a.href = window.URL.createObjectURL(blob)
        a.dataset.downloadurl =  ['text/json', a.download, a.href].join(':')
        e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
        a.dispatchEvent(e)
    }
})(console)

cssprettifier.js #   (view raw)

Script for unminifying and prettifying a CSS file written by addyosmani and sindresorhus. View the cssprettifier-bookmarklet project on github.

snippets/cssprettifier/cssprettifier

// cssprettifier.js
// https://github.com/bgrins/devtools-snippets
// Unminify and prettify a CSS file.

/*
 * cssprettifier-bookmarklet
 * Copyright (c) 2013 Addy Osmani, Sindre Sorhus
 * CSSBeautify (c) Sencha, Ariya Hidayat
 * Prism (c) Lea Verou
 * Licensed under the MIT license.
 */
 /*globals document:true*/
(function () {
  'use strict';

  if (document.body.childNodes.length !== 1) {
       console.log("CSS Prettify: This page doesn't appear to be a stylesheet.  Make sure you run this on a css file");
       return;
  }

  // cssbeautify
  (function(){"use strict";function a(a,b){function s(a){return" "===a||"\n"===a||"   "===a||"\r"===a||"\f"===a}function t(a){return"'"===a||'"'===a}function u(a){return h>="a"&&"z">=h||h>="A"&&"Z">=h||h>="0"&&"9">=h||"-_*.:#".indexOf(a)>=0}function v(){var a;for(a=m;a>0;a-=1)g+=c.indent}function w(){g=r(g),p?g+=" {":(g+="\n",v(),g+="{"),"\n"!==i&&(g+="\n"),m+=1}function x(){var a;m-=1,g=r(g),q&&(a=g.charAt(g.length-1),";"!==a&&"{"!==a&&(g+=";")),g+="\n",v(),g+="}",f.push(g),g=""}var c,f,h,i,j,k,l,m,n,o,r,d=0,e=a.length,g="",p=!0,q=!1;for(c=arguments.length>1?b:{},c.indent===void 0&&(c.indent="    "),"string"==typeof c.openbrace&&(p="end-of-line"===c.openbrace),"boolean"==typeof c.autosemicolon&&(q=c.autosemicolon),r=String.prototype.trimRight?function(a){return a.trimRight()}:function(a){return a.replace(/\s+$/,"")},l={Start:0,AtRule:1,Block:2,Selector:3,Ruleset:4,Property:5,Separator:6,Expression:7,URL:8},m=0,k=l.Start,o=!1,f=[],a=a.replace(/\r\n/g,"\n");e>d;)if(h=a.charAt(d),i=a.charAt(d+1),d+=1,t(n))g+=h,h===n&&(n=null),"\"===h&&i===n&&(g+=i,d+=1);else if(t(h))g+=h,n=h;else if(o)g+=h,"*"===h&&"/"===i&&(o=!1,g+=i,d+=1);else if("/"!==h||"*"!==i){if(k===l.Start){if(0===f.length&&s(h)&&0===g.length)continue;if(" ">=h||h.charCodeAt(0)>=128){k=l.Start,g+=h;continue}if(u(h)||"["===h||"@"===h){if(j=r(g),0===j.length)f.length>0&&(g="\n\n");else if("}"===j.charAt(j.length-1)||";"===j.charAt(j.length-1))g=j+"\n\n";else for(;;){if(i=g.charAt(g.length-1)," "!==i&&9!==i.charCodeAt(0))break;g=g.substr(0,g.length-1)}g+=h,k="@"===h?l.AtRule:l.Selector;continue}}if(k!==l.AtRule)if(k!==l.Block)if(k!==l.Selector)if(k!==l.Ruleset)if(k!==l.Property)if(k!==l.Separator)if(k!==l.Expression)k===l.URL&&")"===h&&g.charAt("\"!==g.length-1)?(g+=h,k=l.Expression):g+=h;else{if("}"===h){x(),k=l.Start,m>0&&(k=l.Block);continue}if(";"===h){g=r(g),g+=";\n",k=l.Ruleset;continue}if(g+=h,"("===h&&"l"===g.charAt(g.length-2)&&"r"===g.charAt(g.length-3)&&"u"===g.charAt(g.length-4)){k=l.URL;continue}}else{if(!s(h)){g+=h,k=l.Expression;continue}t(i)&&(k=l.Expression)}else{if(":"===h){g=r(g),g+=": ",k=l.Expression,s(i)&&(k=l.Separator);continue}if("}"===h){x(),k=l.Start,m>0&&(k=l.Block);continue}g+=h}else{if("}"===h){x(),k=l.Start,m>0&&(k=l.Block);continue}if("\n"===h){g=r(g),g+="\n";continue}if(!s(h)){g=r(g),g+="\n",v(),g+=h,k=l.Property;continue}g+=h}else{if("{"===h){w(),k=l.Ruleset;continue}if("}"===h){x(),k=l.Start;continue}g+=h}else{if(u(h)){if(j=r(g),0===j.length)f.length>0&&(g="\n\n");else if("}"===j.charAt(j.length-1))g=j+"\n\n";else for(;;){if(i=g.charAt(g.length-1)," "!==i&&9!==i.charCodeAt(0))break;g=g.substr(0,g.length-1)}v(),g+=h,k=l.Selector;continue}if("}"===h){x(),k=l.Start;continue}g+=h}else{if(";"===h){g+=h,k=l.Start;continue}if("{"===h){j=r(g),w(),k="@font-face"===j?l.Ruleset:l.Block;continue}g+=h}}else o=!0,g+=h,g+=i,d+=1;return g=f.join("")+g}"undefined"!=typeof exports?module.exports=exports=a:"object"==typeof window&&(window.cssbeautify=a)})();
  // prism
  (function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{type:function(e){return Object.prototype.toString.call(e).match(/\[object (\w+)\]/)[1]},clone:function(e){var n=t.util.type(e);switch(n){case"Object":var r={};for(var i in e)e.hasOwnProperty(i)&&(r[i]=t.util.clone(e[i]));return r;case"Array":return e.slice()}return e}},languages:{extend:function(e,n){var r=t.util.clone(t.languages[e]);for(var i in n)r[i]=n[i];return r},insertBefore:function(e,n,r,i){i=i||t.languages;var s=i[e],o={};for(var u in s)if(s.hasOwnProperty(u)){if(u==n)for(var a in r)r.hasOwnProperty(a)&&(o[a]=r[a]);o[u]=s[u]}return i[e]=o},DFS:function(e,n){for(var r in e){n.call(e,r,e[r]);t.util.type(e)==="Object"&&t.languages.DFS(e[r],n)}}},highlightAll:function(e,n){var r=document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code');for(var i=0,s;s=r[i++];)t.highlightElement(s,e===!0,n)},highlightElement:function(r,i,s){var o,u,a=r;while(a&&!e.test(a.className))a=a.parentNode;if(a){o=(a.className.match(e)||[,""])[1];u=t.languages[o]}if(!u)return;r.className=r.className.replace(e,"").replace(/\s+/g," ")+" language-"+o;a=r.parentNode;/pre/i.test(a.nodeName)&&(a.className=a.className.replace(e,"").replace(/\s+/g," ")+" language-"+o);var f=r.textContent;if(!f)return;f=f.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/\u00a0/g," ");var l={element:r,language:o,grammar:u,code:f};t.hooks.run("before-highlight",l);if(i&&self.Worker){var c=new Worker(t.filename);c.onmessage=function(e){l.highlightedCode=n.stringify(JSON.parse(e.data));l.element.innerHTML=l.highlightedCode;s&&s.call(l.element);t.hooks.run("after-highlight",l)};c.postMessage(JSON.stringify({language:l.language,code:l.code}))}else{l.highlightedCode=t.highlight(l.code,l.grammar);l.element.innerHTML=l.highlightedCode;s&&s.call(r);t.hooks.run("after-highlight",l)}},highlight:function(e,r){return n.stringify(t.tokenize(e,r))},tokenize:function(e,n){var r=t.Token,i=[e],s=n.rest;if(s){for(var o in s)n[o]=s[o];delete n.rest}e:for(var o in n){if(!n.hasOwnProperty(o)||!n[o])continue;var u=n[o],a=u.inside,f=!!u.lookbehind||0;u=u.pattern||u;for(var l=0;l<i.length;l++){var c=i[l];if(i.length>e.length)break e;if(c instanceof r)continue;u.lastIndex=0;var h=u.exec(c);if(h){f&&(f=h[1].length);var p=h.index-1+f,h=h[0].slice(f),d=h.length,v=p+d,m=c.slice(0,p+1),g=c.slice(v+1),y=[l,1];m&&y.push(m);var b=new r(o,a?t.tokenize(h,a):h);y.push(b);g&&y.push(g);Array.prototype.splice.apply(i,y)}}}return i},hooks:{all:{},add:function(e,n){var r=t.hooks.all;r[e]=r[e]||[];r[e].push(n)},run:function(e,n){var r=t.hooks.all[e];if(!r||!r.length)return;for(var i=0,s;s=r[i++];)s(n)}}},n=t.Token=function(e,t){this.type=e;this.content=t};n.stringify=function(e){if(typeof e=="string")return e;if(Object.prototype.toString.call(e)=="[object Array]")return e.map(n.stringify).join("");var r={type:e.type,content:n.stringify(e.content),tag:"span",classes:["token",e.type],attributes:{}};r.type=="comment"&&(r.attributes.spellcheck="true");t.hooks.run("wrap",r);var i="";for(var s in r.attributes)i+=s+'="'+(r.attributes[s]||"")+'"';return"<"+r.tag+' class="'+r.classes.join(" ")+'" '+i+">"+r.content+"</"+r.tag+">"};if(!self.document){self.addEventListener("message",function(e){var n=JSON.parse(e.data),r=n.language,i=n.code;self.postMessage(JSON.stringify(t.tokenize(i,t.languages[r])));self.close()},!1);return}var r=document.getElementsByTagName("script");r=r[r.length-1];if(r){t.filename=r.src;document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)}})();;
  Prism.languages.css={comment:/\/\*[\w\W]*?\*\//g,atrule:/@[\w-]+?(\s+[^;{]+)?(?=\s*{|\s*;)/gi,url:/url\((["']?).*?\)/gi,selector:/[^\{\}\s][^\{\}]*(?=\s*\{)/g,property:/(\b|\B)[a-z-]+(?=\s*:)/ig,string:/("|')(\?.)*?/g,important:/\B!important\b/gi,ignore:/&(lt|gt|amp);/gi,punctuation:/[\{\};:]/g};Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{style:{pattern:/(&lt;|<)style[\w\W]*?(>|&gt;)[\w\W]*?(&lt;|<)\/style(>|&gt;)/ig,inside:{tag:{pattern:/(&lt;|<)style[\w\W]*?(>|&gt;)|(&lt;|<)\/style(>|&gt;)/ig,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.css}}});

  var prismStyle = document.createElement('style');
  var beautified = cssbeautify(document.body.textContent, {autosemicolon: true});
  var highlighted = Prism.highlight(beautified, Prism.languages.css);

  prismStyle.textContent = 'code[class*="language-"],pre[class*="language-"]{color:black;text-shadow:0 1px white;font-family:Consolas,Monaco,#39;Andale Mono#39;,monospace;direction:ltr;text-align:left;white-space:pre;word-spacing:normal;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none;}@media print{code[class*="language-"],pre[class*="language-"]{text-shadow:none;}}pre[class*="language-"]{padding:1em;margin:.5em 0;overflow:auto;}:not(pre) > code[class*="language-"],pre[class*="language-"]{background:#f5f2f0;}:not(pre) > code[class*="language-"]{padding:.1em;border-radius:.3em;}.token.comment,.token.prolog,.token.doctype,.token.cdata{color:slategray;}.token.punctuation{color:#999;}.namespace{opacity:.7;}.token.property,.token.tag,.token.boolean,.token.number{color:#905;}.token.selector,.token.attr-name,.token.string{color:#690;}.token.operator,.token.entity,.token.url,.language-css .token.string,.style .token.string{color:#a67f59;background:hsla(0,0%,100%,.5);}.token.atrule,.token.attr-value,.token.keyword{color:#07a;}.token.regex,.token.important{color:#e90;}.token.important{font-weight:bold;}.token.entity{cursor:help;}';

  document.head.innerHTML = '';
  document.head.appendChild(prismStyle);
  document.body.innerHTML = '<code><pre>' + highlighted + '</pre></code>';

})();

cssreload.js #   (view raw)

Reloads all CSS files on the page. It does so by adding a random GET parameter onto each stylesheet’s href attribute.

snippets/cssreload/cssreload

// cssreload.js
// https://github.com/bgrins/devtools-snippets
// Removes then reloads all the CSS files in the current page

(function () {

  function insertAfter(newElement, targetElement) {
    var parent = targetElement.parentNode;
    if (parent.lastChild == targetElement) {
      parent.appendChild(newElement);
    } else {
      parent.insertBefore(newElement, targetElement.nextSibling);
    }
  }

  function reloadStyleSheet(stylesheet) {
    var element = stylesheet.ownerNode;
    var clone = element.cloneNode(false);
    clone.href = addRandomToUrl(clone.href);
    clone.addEventListener("load", function() {
      if (element.parentNode) {
        element.parentNode.removeChild(element);
      }
    });
    insertAfter(clone, element);
  }

  function addRandomToUrl(input) {
    // prevent CSS caching
    var hasRnd = /([?&])_=[^&]*/,
      hasQueryString = /\?/,
      hasHash = /(.+)#(.+)/,
      hash = null,
      rnd = Math.random();

    var hashMatches = input.match(hasHash);
    if (hashMatches) {
      input = hashMatches[1];
      hash = hashMatches[2];
    }
    url = hasRnd.test(input) ?
    input.replace(hasRnd, "$1_=" + rnd) :
    input + (hasQueryString.test(input) ? "&" : "?") + "_=" + rnd;
    if (hash) url += '#' + hash;
    return url;
  }

  [].forEach.call(document.styleSheets, function(styleSheet) {
    if (!styleSheet.href) return;
    console.log('reload ' + styleSheet.href);
    reloadStyleSheet(styleSheet);
  });

})();

dataurl.js #   (view raw)

Convert all images on the page to data URLs. Note: this only works for images that are on the same domain as the current page.

snippets/dataurl/dataurl

// dataurl.js
// https://github.com/bgrins/devtools-snippets
// Print out data URLs for all images / canvases on the page.

(function() {

  console.group("Data URLs");

  [].forEach.call(document.querySelectorAll("img"), function(i) {
    var c = document.createElement("canvas");
    var ctx = c.getContext("2d");
    c.width = i.width;
    c.height = i.height;

    try {
      ctx.drawImage(i, 0, 0);
      console.log(i, c.toDataURL());
    }
    catch(e) {
      console.log(i, "No Permission - try opening this image in a new tab and running the snippet again?", i.src);
    }
  });

  [].forEach.call(document.querySelectorAll("canvas"), function(c) {
    try {
      console.log(c, c.toDataURL());
    }
    catch(e) {
      console.log(c, "No Permission");
    }
  });

  console.groupEnd("Data URLs");

})();

formcontrols.js #   (view raw)

Shows all html form elements with their values and types in a nice table. Adds a new table for each form on the page. Implementation by Kienz.

snippets/formcontrols/formcontrols

// formcontrols.js
// https://github.com/bgrins/devtools-snippets
// Print out forms and their controls

(function() {

  var forms = document.querySelectorAll("form");

  for (var i = 0, len = forms.length; i < len; i++) {
    var tab = [ ];

    console.group("HTMLForm quot;" + forms[i].name + "quot;: " + forms[i].action);
    console.log("Element:", forms[i], "\nName:    "+forms[i].name+"\nMethod:  "+forms[i].method.toUpperCase()+"\nAction:  "+forms[i].action || "null");

    ["input", "textarea", "select"].forEach(function (control) {
      [].forEach.call(forms[i].querySelectorAll(control), function (node) {
        tab.push({
          "Element": node,
          "Type": node.type,
          "Name": node.name,
          "Value": node.value,
          "Pretty Value": (isNaN(node.value) || node.value === "" ? node.value : parseFloat(node.value))
        });
      });
    });

    console.table(tab);
    console.groupEnd();
  }
})();

html_i18n_content.js #   (view raw)

Generate downloadable files, based on location.href, for i18n of Chrome App or Extension:

  • messages.json containing chrome.i18n messages (with placeholders) for element.innerText and input[value]
  • location.href with i18n-content tags added (text and placeholders preserved to ease round-tripping changes)
  • applyChromeI18nMessages.js to include in location.href to initialize localized messages on load

Implementation by anaran.

snippets/html_i18n_content/html_i18n_content

// html_i18n_content.js
// https://github.com/anaran/devtools-snippets
// Generate downloadable chrome.i18n messages file for location.href.
// See http://developer.chrome.com/extensions/i18n.html
// Messages are based on innerText or value attribute, keyed by class attribute.
// $Placeholders$ are added to messages.json as well.
// Generate downloadable HTML file with corresponding i18n-content attributes.
// Generate downloadable script to initialize localized messages on load.
try {
    console.log(location.href);
    var hsh = {};
    var replacer = [];
    replacer.push('message', 'description', 'placeholders', 'content', 'example');
    var addKeyValuePlaceHolders = function(messages, key, value) {
        messages[key] = {
            'message': value,
                'description': value
        };
        var placeHolders = value.match(/\$([^$]+)\$/g);
        if (placeHolders) {
            messages[key]['placeholders'] = {};
            for (j = 0, jlen = placeHolders.length; j < jlen; j++) {
                var placeHolderName = placeHolders[j].replace(/\$/g, '').toLowerCase();
                messages[key]['placeholders'][placeHolderName] = {
                    'content': '$' + (j + 1),
                        'example': value
                };
                if (!replacer.some(function(value) {
                    return value === placeHolderName;
                })) {
                    replacer.push(placeHolderName);
                }
            }
        }
        return messages;
    };
    var i18nForValueAttribute = function(select, messages) {
        var nds = document.querySelectorAll(select);
        for (i = 0, len = nds.length; i < len; i++) {
            var value = nds[i].getAttribute('value');
            if (value) {
                var key = nds[i].className;
                if (key && value) {
                    nds[i].setAttribute('i18n-content', key);
                    // Better keep value for round-tripping.
                    // nds[i].setAttribute('value', '');
                    messages = addKeyValuePlaceHolders(messages, key, value);
                }
            }
        }
    };
    var i18nForInnerText = function(select, messages) {
        var nds = document.querySelectorAll(select);
        for (i = 0, len = nds.length; i < len; i++) {
            // TODO Please note we use .innerText because it preserves newline characters in Chrome, while .textContent loses them.
            var value = nds[i].innerText;
            if (nds[i].childElementCount === 0 && value) {
                var key = nds[i].className;
                var value = value.replace(/\s+/g, ' ').trim();
                if (key && value) {
                    nds[i].setAttribute('i18n-content', key);
                    // Better keep value for round-tripping.
                    // nds[i].innerText = '';
                    messages = addKeyValuePlaceHolders(messages, key, value);
                }
            }
        }
        var i18nNodes = document.querySelectorAll(select + '[i18n-content]');
        for (i = 0, len = i18nNodes.length; i < len; i++) {
            if (i18nNodes[i].hasAttribute('i18n-content') && i18nNodes[i].childElementCount > 0) {
                console.warn('HTML elements have been added to\n%O\nPlease only use text and named character references (%o)!',
                i18nNodes[i], 'https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Introduction#Named_character_references');
            }
        }
    };
    i18nForInnerText('*', hsh);
    i18nForValueAttribute('input[value]', hsh);
    Object.getOwnPropertyNames(hsh).sort().forEach(function(value) {
        replacer.push(value);
    });
    var messagesString = JSON.stringify(hsh, replacer, 4);
    var htmlFileText = '<!DOCTYPE ' + document.doctype.name + '>\n' + document.documentElement.outerHTML;
    var htmlFileName = location.pathname.split('/').pop();
    var makeDownloadLink = function(data, filename, style) {
        var blob = new window.Blob([data], {
            'type': 'text/utf-8'
        });
        var a = document.createElement('a');
        a.innerText = 'Download ' + filename;
        a.href = URL.createObjectURL(blob);
        a.download = filename;
        document.body.appendChild(a);
        a.setAttribute('style', style);
        a.onclick = function() {
            setTimeout(function() {
                document.body.removeChild(a);
            }, 500);
        };
    }
    var applyChromeI18nMessages = function() {
        // This generated function body is wrapped in (...)(); to execute on load.
        // This will only install the onreadystatechange event handler
        // to be run when the document load is complete.
        // Load this file into the associated HTML file by including
        // <script src="applyChromeI18nMessages.js"></script>
        // in its head element.
        try {
            document.addEventListener('readystatechange', function(event) {
                if (event.target.readyState !== 'complete') {
                    return;
                }
                if (!chrome.i18n) {
                    console.warn('chrome.i18n is undefined.\n%s\nis %cnot%c viewed as part of a chrome extension.', document.URL, 'font-weight: bold', '');
                    return;
                }
                (function() {
                    var nds = document.querySelectorAll('[i18n-content]');
                    for (i = 0, len = nds.length; i < len; i++) {
                        var value = nds[i].getAttribute('value');
                        var key = nds[i].getAttribute('i18n-content');
                        if (value === null) {
                            // TODO Please note we use .innerText because it preserves newline characters in Chrome, while .textContent loses them.
                            nds[i].innerText = chrome.i18n.getMessage(key);
                        } else {
                            nds[i].setAttribute('value', chrome.i18n.getMessage(key));
                        }
                    }
                })();
            }, false);
        } catch (exception) {
            window.alert('exception.stack: ' + exception.stack);
            console.log((new Date()).toJSON(), 'exception.stack:', exception.stack);
        }
    };
    makeDownloadLink(messagesString, 'messages.json', 'position:fixed;top:2em;left:50%;opacity:0.5');
    makeDownloadLink(htmlFileText, htmlFileName, 'position:fixed;top:4em;left:50%;opacity:0.5');
    makeDownloadLink('(' + applyChromeI18nMessages + ')();', 'applyChromeI18nMessages.js', 'position:fixed;top:6em;left:50%;opacity:0.5');
} catch (exception) {
    console.log(exception.stack);
}

insert-css.js #   (view raw)

Defines insertCss() function which injects a snippet of CSS into the current page. After running the snippet, you can call it as follows: insertCss('span { color: red !important; }');

snippets/insert-css/insert-css

// insert-css.js
// https://github.com/bgrins/devtools-snippets
// Injects a snippet of CSS into the current page.

function insertCss(code) {
  var style = document.createElement('style');
  style.type = 'text/css';

  if (style.styleSheet) {  // IE
    style.styleSheet.cssText = code;
  } else { // Other browsers
    style.innerHTML = code;
  }
  document.getElementsByTagName("head")[0].appendChild( style );
}

// Feel free to extend this snippet with your favorite CSS snippets.
// Here's an example which makes the current page high contrast.
// Notice the trailing backslashes, used to define multiline strings.
function insertCssHighContrast() {
  var css = '\
    * { background: white ! important; color: black !important } \
    :link, :link * { color: #0000EE !important } \
    :visited, :visited * { color: #551A8B !important } \
  ';
    insertCss(css);
}

jquerify.js #   (view raw)

Includes jQuery onto a page if it is not yet included.

snippets/jquerify/jquerify

// jquerify.js
// https://github.com/bgrins/devtools-snippets
// Add jQuery to any page that does not have it already.

(function () {

  if ( !window.jQuery ) {
    var dollarInUse = !!window.$;
    var s = document.createElement('script');
    s.setAttribute('src', '//ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js');
    s.addEventListener('load', function(){
      console.log('jQuery loaded!');

      if(dollarInUse) {
        jQuery.noConflict();
        console.log('`$` already in use; use `jQuery`');
      }
    });

    document.body.appendChild(s);
  }

})();

log.js #   (view raw)

Adds a log function to window object.

snippets/log/log

// log.js
// https://github.com/bgrins/devtools-snippets
// Adds a `log` function to window object.
// http://www.briangrinstead.com/blog/console-log-helper-function

(function() {

  window.log = Function.prototype.bind.call(console.log, console);

})();

log-globals.js #   (view raw)

Logs your global variables to the console. Useful for finding leaked global variables.

log-globals created by Sindre Sorhus.

screenshot

/*
	log-globals
	by Sindre Sorhus
	https://github.com/sindresorhus/log-globals
	MIT License
*/
(function () {
	'use strict';

	function getIframe() {
		var el = document.createElement('iframe');
		el.style.display = 'none';
		document.body.appendChild(el);
		var win = el.contentWindow;
		document.body.removeChild(el);
		return win;
	}

	function detectGlobals() {
		var iframe = getIframe();
		var ret = Object.create(null);

		for (var prop in window) {
			if (!(prop in iframe)) {
				ret[prop] = window[prop];
			}
		}

		return ret;
	}

	console.log(detectGlobals());
})();

overlay.js #   (view raw)

Add an semi-transparent image overlay over your page to have pixel-perfect integration with your designs.

var storageKey = 'overlay-default';
var overlayDefault = localStorage.getItem(storageKey) || '';
var url = prompt('paste overlay url', overlayDefault);
if (url) {
    localStorage.setItem(storageKey, url);
    var overlay = document.createElement('div');
    overlay.style.position = 'absolute';
    overlay.style.left = 0;
    overlay.style.top = 0;
    overlay.style.width = '100%';
    overlay.style.height='100%';
    overlay.style.backgroundImage = 'url(' + url + ')';
    overlay.style.backgroundSize = 'cover';
    overlay.style.zIndex=10000;
    overlay.style.opacity = 0.5;
    document.body.appendChild(overlay);
}

performance.js #   (view raw)

Print out information about the window.performance object. Uses console.table and grouping to organize the information.

snippets/performance/performance

// performance.js
// https://github.com/bgrins/devtools-snippets
// Print out window.performance information.
// https://developer.mozilla.org/en-US/docs/Navigation_timing

(function () {

  var t = window.performance.timing;
  var lt = window.chrome && window.chrome.loadTimes && window.chrome.loadTimes();
  var timings = [];

  timings.push({
    label: "Time Until Page Loaded",
    time: t.loadEventEnd - t.navigationStart + "ms"
  });
  timings.push({
    label: "Time Until DOMContentLoaded",
    time: t.domContentLoadedEventEnd - t.navigationStart + "ms"
  });
  timings.push({
    label: "Total Response Time",
    time: t.responseEnd - t.requestStart + "ms"
  });
  timings.push({
    label: "Connection",
    time: t.connectEnd - t.connectStart + "ms"
  });
  timings.push({
    label: "Response",
    time: t.responseEnd - t.responseStart + "ms"
  });
  timings.push({
    label: "Domain Lookup",
    time: t.domainLookupEnd - t.domainLookupStart + "ms"
  });
  timings.push({
    label: "Load Event",
    time: t.loadEventEnd - t.loadEventStart + "ms"
  });
  timings.push({
    label: "Unload Event",
    time: t.unloadEventEnd - t.unloadEventStart + "ms"
  });
  timings.push({
    label: "DOMContentLoaded Event",
    time: t.domContentLoadedEventEnd - t.domContentLoadedEventStart + "ms"
  });
  if(lt) {
    if(lt.wasNpnNegotiated) {
      timings.push({
        label: "NPN negotiation protocol",
        time: lt.npnNegotiatedProtocol
      });
    }
    timings.push({
      label: "Connection Info",
      time: lt.connectionInfo
    });
    timings.push({
      label: "First paint after Document load",
      time: Math.ceil(lt.firstPaintTime - lt.finishDocumentLoadTime) + "ms"
    });
  }

  var navigation = window.performance.navigation;
  var navigationTypes = { };
  navigationTypes[navigation.TYPE_NAVIGATENEXT || 0] = "Navigation started by clicking on a link, or entering the URL in the user agent's address bar, or form submission.",
  navigationTypes[navigation.TYPE_RELOAD] = "Navigation through the reload operation or the location.reload() method.",
  navigationTypes[navigation.TYPE_BACK_FORWARD] = "Navigation through a history traversal operation.",
  navigationTypes[navigation.TYPE_UNDEFINED] = "Navigation type is undefined.",

  console.group("window.performance");

  console.log(window.performance);

  console.group("Navigation Information");
  console.log(navigationTypes[navigation.type]);
  console.log("Number of redirects that have taken place: ", navigation.redirectCount)
  console.groupEnd("Navigation Information");

  console.group("Timing");
  console.log(window.performance.timing);
  console.table(timings, ["label", "time"]);
  console.groupEnd("Timing");

  console.groupEnd("window.performance");

})();

plainforms.js #   (view raw)

HTML5 Forms are great, but sometimes you don’t want the browser to validate or present special controls for them. For instance, if you want to test server-side validation of some fields, you do not want the browser to prevent invalid data for that field type. This snippet finds all of the HTML5 input elements, sets their type attributes to “text” (and keeps any values that were set), and removes any validations enforced by the browser. Implementation by stroebjo.

snippets/plainforms/plainforms

// plainforms.js
// https://github.com/bgrins/devtools-snippets
// Remove HTML5 form features (validations and special input types).

(function () {

  ['maxlength', 'required', 'min', 'max', 'pattern', 'step' ].forEach(function (attr) {
    [].forEach.call(document.querySelectorAll("[" + attr + "]"), function (node) {
      node.removeAttribute(attr);
    });
  });

  ['tel', 'url', 'email', 'datetime', 'date', 'month', 'week', 'time', 'datetime-local', 'number', 'range', 'color'].forEach(function (type) {
    [].forEach.call(document.querySelectorAll("input[type=" + type + "]"), function (node) {
      node.setAttribute('type', 'text');
    });
  });

  console.info("All HTML5 form validations have been removed.");
})();

querystringvalues.js #   (view raw)

Print a table of query string (GET) values. This can be helpful, especially when trying to read the values from a long or complicated URL that may otherwise need to be pasted into another editor to read. Implementation by mattpass.

snippets/querystringvalues/querystringvalues

// querystringvalues.js
// https://github.com/bgrins/devtools-snippets
// Print out key/value pairs from querystring.

(function() {

  var url = location;
  var querystring = location.search.slice(1);
  var tab = querystring.split("&").map(function(qs) {
    return { "Key": qs.split("=")[0], "Value": qs.split("=")[1], "Pretty Value": decodeURIComponent(qs.split("=")[1]).replace(//g," ") }
  });

  console.group("Querystring Values");
  console.log("URL: "+url+"\nQS:  "+querystring);
  console.table(tab);
  console.groupEnd("Querystring Values");

})();

showheaders.js #   (view raw)

Pretty prints the HTTP headers for the current page into the console. Uses console.table.

snippets/showheaders/showheaders

// showheaders.js
// https://github.com/bgrins/devtools-snippets
// Print out response headers for current URL.

(function() {

  var request=new XMLHttpRequest();
  request.open('HEAD',window.location,true);
  
  request.onload = request.onerror = function () {
    var headers = request.getAllResponseHeaders();
    var tab = headers.split("\n").map(function(h) {
      return { "Key": h.split(": ")[0], "Value": h.split(": ")[1] }
    }).filter(function(h) { return h.Value !== undefined; });

    console.group("Request Headers");
    console.log(headers);
    console.table(tab);
    console.groupEnd("Request Headers");
  };
  
  request.send(null);

})();

viewcookies.js #   (view raw)

Shows all cookies stored in document.cookies in a console.table.

snippets/viewcookies/viewcookies

// viewcookies.js
// https://github.com/bgrins/devtools-snippets
// Shows all cookies stored in document.cookies in a console.table

(function() {
  'use strict';

  window.viewCookies = function() {
    if (document.cookie) {
      const cookies = document.cookie
        .split(/; ?/)
        .map(s => {
          const [ , key, value ] = s.match(/^(.*?)=(.*)$/);
          return {
            key,
            value: decodeURIComponent(value)
          };
        });

      console.table(cookies);
    }
    else {
      console.warn('document.cookie is empty!');
    }
  };
})();

window.viewCookies();

wrapelement.js #   (view raw)

Exposes a window function wrapElement(element, tag). The first parameter is either a DOM Node, or a selector string, and the second parameter is the tag name for the wrapping element. Implementation by gkatsev.

snippets/wrapelement/wrapelement

// wrapelement.js
// https://github.com/bgrins/devtools-snippets
// Wrap a given element in a given type of element
// wrapElement('.foo', 'h1');
// wrapElement(document.querySelector('#bar'), 'div');
//
// LICENSE: [MIT](http://gkatsev.mit-license.org)

(function() {
  window.wrapElement = function(el, whatToWrapIn) {
    var newParent = document.createElement(whatToWrapIn),
        oldParent,
        nextSibling;

    if (typeof el === 'string') {
      el = document.querySelector(el);
    }

    oldParent = el.parentNode;
    nextSibling = el.nextSibling;
    newParent.appendChild(el);
    if (nextSibling) {
      oldParent.insertBefore(newParent, nextSibling);
    } else {
      oldParent.appendChild(newParent);
    }
  }

})();