html-inspector
HTML Inspector
⚠️ NOTE: I no longer have time to maintain this project, respond to issues, or address PRs. If anyone else would like to take over as a maintainer, please contact me.
- Getting Started
- Configuring HTML Inspector
- Built-in Rules
- Writing Your Own Rules
- Overriding Rules Configurations
- Browser Support
- Running the Tests
- Contributing
- FAQs
- Third Party Rules
HTML Inspector is a highly-customizable, code quality tool to help you (and your team) write better markup. It aims to find a balance between the uncompromisingly strict W3C validator and having absolutely no rules at all (the unfortunate reality for most of us).
HTML Inspector is opinionated, but every rule is completely customizable, so you can take what you like and change what you don’t. It’s also easy to extend, allowing teams to write their own rules to enforce their chosen conventions.
For a more formal introduction, please refer to this blog post which goes into more detail as to why HTML Inspector was created and why you should consider using it.
Getting Started
The easiest way to try out HTML Inspector is to link to the source file hosted on CDNJS:
<script src="http://cdnjs.cloudflare.com/ajax/libs/html-inspector/0.8.2/html-inspector.js"></script>
It can also be installed via NPM or Bower:
# NPM (for command line usage) npm install -g html-inspector # Inspect a file from the command line # Note: the CLI requires phantomjs to be installed # http://phantomjs.org/download.html htmlinspector path/to/file.html # View the CLI options htmlinspector --help # Bower (for browser usage) bower install html-inspector
If you clone the Github repo, just use the file named html-inspector.js
in the project root.
Once HTML Inspector is added, you can run HTMLInspector.inspect()
to see the results. Calling inspect
with no options will load all rules and run them with their default configuration options.
<script src="path/to/html-inspector.js"></script> <script> HTMLInspector.inspect() </script>
After the script runs, any errors will be reported to the console (unless you change this behavior). Here’s an example of what you might see:
Make sure you call inspect
after any other DOM-altering scripts have finished running or those alterations won’t get inspected.
Configuring HTML Inspector
By default, HTML Inspector runs all added rules, starts traversing from the <html>
element, and logs errors to the console when complete, but all of this can be customized.
The inspect
method takes a config object to allow you to change any of this behavior. Here are the config options:
- domRoot: (selector | element) the DOM element to start traversing from
-
useRules: (Array) a list of rule names to run when inspecting. Defaults to running all rules not excluded via
excludeRules
-
excludeRules: (Array) a list of rule names not to run when inspecting. If
useRules
andexcludeRules
are both set, the excluded rules are removed from the list of rules to use. - excludeElements: (selector | element | Array) any DOM element that matches the selector, element, or list of selectors/elements will be excluded from traversal (note: its descendants will still be traversed).
- excludeSubTrees: (selector | element | Array) the descendants of any DOM element that matches the selector, element, or list of selectors/elements will be excluded from traversal.
- onComplete: (Function) the callback to be invoked when the inspection is finished. The function is passed an array of errors that were reported by individual rules.
Here are the default configuration values:
config: { domRoot: "html", useRules: null, excludeRules: null, excludeElements: "svg", excludeSubTrees: ["svg", "iframe"], onComplete: function(errors) { errors.forEach(function(error) { console.warn(error.message, error.context) }) } }
Here is how you might override the default configurations:
HTMLInspector.inspect({ domRoot: "body", excludeRules: ["some-rule-name", "some-other-rule-name"], excludeElements: ["svg", "iframe"], onComplete: function(errors) { errors.forEach(function(error) { // report errors to external service... }) } })
For convenience, some of the config options may be passed as single arguments. If .inspect()
receives an argument that is an array, it is assumed to be the useRules
option; if it’s a string or DOM element, it’s assumed to be the domRoot
option; and if it’s a function, it’s assumed to be the onComplete
callback.
// only set the useRules options HTMLInspector.inspect(["some-rule-name", "some-other-rule-name"]) // only set the domRoot HTMLInspector.inspect("#content") // only set the onComplete callback HTMLInspector.inspect(function(errors) { errors.forEach(function(error) { // report errors to an external service... }) })
Built-in Rules
HTML Inspector ships with a set of built-in rules that fall into one of three main categories: validation, best-practices, and convention.
Each rule is registered via a unique string identifier that can be used to include or exclude it at inspection time.
Here is the full list of built-in rules by their identifiers:
# validation rules validate-elements validate-element-location validate-attributes duplicate-ids unique-elements # best-practices inline-event-handlers script-placement unused-classes unnecessary-elements # convention bem-conventions
The following is a more in-depth explanation of each rule:
Validation
HTML Inspector is different than a markup validator. Validators parse static markup, while HTML Inspector runs on a live DOM. This makes it a lot more powerful, but there are some drawbacks as well. Because HTML Inspector runs after the browser has parsed your HTML, any mistakes the browser has forgiven will not be seen by HTML Inspector.
As a result HTML Inspector should not be seen as a replacement for validation.; it’s simply another tool in the toolbox. That being said, there is still a lot that it can do (and does) to validate your markup.
Here are the validation rules that ship with HTML Inspector. (Expect this list to get more comprehensive in the future.)
-
Validate Elements: Inspects each element in the DOM and reports any elements that are invalid or obsolete. This will catch simple things like misspelled tags (
<il>
instead of<li>
), and it will inform you of deprecated tags (like<center>
,<font>
, and more recently<hgroup>
). Any element you don’t want to be warned about can be whitelisted. -
Validate Element Location: Make sure that elements don’t appear as children of parents they’re not allowed to descend from. An example of this is a block element like
<div>
appearing as the child of an inline element like<span>
. -
Validate Attributes: Like validating elements, this rule will let you know if you’re using attributes that don’t belong on a particular element or perhaps don’t belong on any element. If your project uses custom attributes (like
ng-*
in AngularJS), these can be whitelisted. - Duplicate IDs: Warn if non-unique IDs are found on the same page.
-
Unique Elements: Warn if elements that should be unique (like
<title>
and<main>
) appear more than once in the document.
Best Practices
Some markup may be perfectly valid but uses practices that are commonly considered to be poor or outdated. The following rules check for these types of things. (Note that everything in this list is subjective and optional.)
-
Inline Event Handlers: Warn if inline event handlers, like
onclick="return false"
are found in the document. Inline event handlers are hard to manage, hard to debug, and completely non-reusable. -
Script Placement: Warn if
<script>
elements appear anywhere other than right before the closing</body>
tag. Because JavaScript is a blocking resource, adding<script>
elements anywhere other than the end of the document may delay the loading of the page. If a script must appear somewhere other than the end of the document, it can be whitelisted. -
Unused Classes: Sometimes you’ll remove a CSS rule from your stylesheet but forget to remove the class from the HTML. The “unused-classes” rule compares all the class selectors in the CSS to the classes in the HTML and reports any that aren’t being used.
Classes that are in the HTML as JavaScript hooks can be ignored via a whitelist. By default, any class prefixed withjs-
,language-
, orsupports-
is whitelisted. More information on the rationale behind this rule can be found here. -
Unnecessary Elements: Anytime you have a plain
<div>
or<span>
element in the HTML with no class, ID, or any other attribute, it’s probably unnecessary or a mark of poor design.
Elements with no semantic meaning should only be used for presentation. If the element has no attributes but is used for styling, it must be done through a rule like.some-class > div { }
, which is just asking for trouble. Again, more information can be found here.
Convention
The real power of HTML Inspector lies in its ability to enforce your team’s chosen conventions. If you’ve decided that all groups of links should be contained in a <nav>
element, or all <section>
elements must contain a heading, you can write those rules and an error will be thrown when someone breaks them.
Because conventions are usually specific to individual teams, there’s only one built-in rule in this category, but hopefully it’ll get you thinking about rules your team could use.
-
BEM: The increasingly popular BEM (block, element, modifier) methodology is a CSS naming convention that is very helpful for large projects. The problem is that using it correctly in the CSS is only half the battle. If it’s not used correctly in the HTML it doesn’t work either.
This rule throws an error when an element class name is used but that element isn’t a descendant of a block by the same name. It also errors when a modifier is used on a block or element without the unmodified class there too.
(Note: there are a few different BEM naming conventions out there. HTML Inspector support the three most…