なぜHTML Validationを気にするべきか
なぜHTML Validationを気にするべきなのでしょうか。
多くのプログラム言語と異なり、HTMLは曖昧な表記でもWebブラウザが計らってくれて上手に表示してくれる技術です。
主要ブラウザで正しく動作しているのであれば、文法上の細かな話は気にするべきではないと考えるかもしれません。
しかし、一方でHTMLの記述間違いがレイアウト崩れにつながったり、動作不良を起こすこともあります。
“HTMLの文法を守る”というルールを課している場合は、機械的に早期に問題を検出し、少ないコストで問題回避を実現できます。
“HTMLの文法を守る”というルールを課していない場合は、深刻な問題が大量のエラーに埋もれてしまい問題の検出を難しくします。
これがHTML Validationを気にするべき理由です。
まとめてやりたいですね
Markup Validation Serviceに1ページずつ指定するのは大変です。
制作中のWebサイトがあれば、まとめて検証したいところですね。
いくつか方法がありますが、npmで配布されているhtml-validateが便利です。
例えば、 ./dist/
配下のHTMLすべて検証する場合は下記のように実行するだけです。
(※グローバルインストールしている場合 npx
は不要)
npx html-validate dist
sitemap.xmlをもとに巡回し検証
Astroなどを用いた静的HTMLであれば、前述のように生成後のHTMLファイルが格納されたディレクトリに対して実行すれば良いですが、サーバーサイドのプログラムが動作している場合もあるでしょう。
sitemap.xml
を出力しているサイトが対象であれば、html-validate
をNode.jsのプログラムから呼び出して、sitemap.xml
を解析して得たURLを巡回するように実装すると良いです。
import http from 'http';
import { parseString } from 'xml2js';
import { HtmlValidate } from 'html-validate';
const validate = new HtmlValidate({
extends: ["html-validate:recommended"],
rules: {
"doctype-style": "off",
"attr-quotes": "off",
"no-trailing-whitespace": "off",
"void-style": "off",
},
});
const ignoreValidateSelector = [
'.any-plugin-name .has-not-valid-element',
];
function fetchHTML(url) {
return new Promise((resolve, reject) => {
http.get(url, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
resolve(data);
});
}).on('error', (err) => {
reject(err);
});
});
}
function parseSitemap(sitemapUrl) {
return new Promise((resolve, reject) => {
http.get(sitemapUrl, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
parseString(data, (err, result) => {
if (err) {
reject(err);
return;
}
const urls = [];
const urlsetUrls = getUrlsetUrls(result);
urls.push(...urlsetUrls);
const sitemapindexUrls = getSitemapindexUrls(result);
if (sitemapindexUrls.length > 0) {
const promises = sitemapindexUrls.map(url => parseSitemap(url));
Promise.all(promises)
.then(results => {
results.forEach(result => {
urls.push(...result);
});
resolve(urls);
})
.catch(reject);
} else {
resolve(urls);
}
});
});
}).on('error', (err) => {
reject(err);
});
});
}
function getUrlsetUrls(result) {
const urls = [];
const urlset = result.urlset;
if (urlset && urlset.url) {
for (const url of urlset.url) {
if (url.loc && url.loc.length > 0) {
urls.push(url.loc[0]);
}
}
}
return urls;
}
function getSitemapindexUrls(result) {
const urls = [];
const sitemapindex = result.sitemapindex;
if (sitemapindex && sitemapindex.sitemap) {
for (const sitemap of sitemapindex.sitemap) {
if (sitemap.loc && sitemap.loc.length > 0) {
urls.push(sitemap.loc[0]);
}
}
}
return urls;
}
async function main() {
const sitemapUrl = 'http://localhost/sitemap.xml';
try {
const urls = await parseSitemap(sitemapUrl);
for (const url of urls) {
try {
// Validate HTML
const html = await fetchHTML(url);
const report = await validate.validateString(html);
if (!report.valid) {
for (const result of report.results) {
console.log(`Validation report for ${url} :`);
for (const message of result.messages) {
if (!ignoreValidateSelector.includes(message.selector)) {
console.log(` Line ${message.line} :`, message.message);
}
}
}
} else {
console.log(`Validation report for ${url} :`, 'No errors found');
}
} catch (error) {
console.error(`Error fetching or validating HTML from ${url}:`, error);
}
}
} catch (error) {
console.error('Error downloading or parsing sitemap:', error);
}
}
main();
オプション設定や特定要素のエラー表示回避策
Webサイトを作るうえで、何らかの制限のためにどうしてもValidationエラーを解決できないケースがあります。
(他のシステムとの接続のため、導入しているプラグインのため等)
全体での設定についてはこのライブラリとして提供されている new HtmlValidate
する際の rules
オプションが便利です。
部分的な要素のエラー出力を抑制するには、上記のサンプルコードで実装しているようにignoreValidateSelector
で、エラーが発生しても無視する要素のセレクタを定義すると良いでしょう。
例では、.any-plugin-name .has-not-valid-element
の要素でのHTML Validationエラーをミュートしています。
複数ページをまとめてLighthouseで検証するで記載した実装もこちらに組み込んで、まとめてGitHub Actionsに組み込んでも良いかもしれませんね。
株式会社add moreではこのように確認に手間がかかることを自動化したり、改善しながら制作や開発を行うエンジニアを募集しています。
ご興味のある方はコーポレートサイトまたはWantedlyからご応募ください。