0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【JS】見出しタグ(<h1>~<h6>)を一括取得する関数(親要素やクラス等の指定にも対応)

Posted at

見出しタグを .querySelectorAll() で取得する場合,いちいち .querySelectorAll( 'h1, h2, h3, h4, h5, h6' ) などと書かなければならず面倒くさい.SCSS なら,

.post--content {
	h1,
	h2,
	h3,
	h4,
	h5,
	h6 {
		&.heading--background-black {
			// なんらかのスタイル指定.
		}
	}
}

みたいにネストさせて書けるセレクタも,いちいち,

document.querySelectorAll(
	'.post__content h1.heading--background-black' +
	'.post__content h2.heading--background-black' +
	'.post__content h3.heading--background-black' +
	'.post__content h4.heading--background-black' +
	'.post__content h5.heading--background-black' +
	'.post__content h6.heading--background-black',
);

といった具合に書かなければならず面倒くさい.こういう面倒くさいのをいちいち手作業でやってると,エラーの原因にもなりがちなので,関数化しました.

なお,本記事における “hx” は、“h1”,“h2”, “h3”, “h4”, “h5”, “h6” の総称です.

コード

TypeScript

headings.ts
/**
 * @author JuthaDDA
 * @param elderSelector - ex. '.post', 'section >', 'hr +', '.meta ~'.
 *     White space should be added after this selector.
 * @param classSelector - ex. '.heading--red', ':hover', ':not(:first-child)'.
 *     White space before this selector should be trimmed
 *     to avoid being interpreted as a nested selector.
 * @param topLevel - should be corrected to an integer in the range 1-6
 *     (ex. 0 to 1, 42 to 6, 1.9 to 1).
 * @param bottomLevel - should be corrected to an integer
 *     in the range from `topLevel` to 6.
 */
interface HxSelectorOptions {
	elderSelector?:string,
	classSelector?:string,
	topLevel?:number,
	bottomLevel?:number
}

/**
 * @author JuthaDDA
 */
interface HxOptions extends HxSelectorOptions {
	root?:Element|Document,
}

/**
 * @author JuthaDDA
 */
const getHxSelector = ( options:HxSelectorOptions ):string => {
	const { min, max, floor } = Math;
	const elderSelector = options.elderSelector ?
		`${ options.elderSelector } ` : '';
	const classSelector = options.classSelector ?
		options.classSelector.trim() : '';
	const topLevel = floor( min( max( options.topLevel || 1, 1 ), 6 ) );
	const bottomLevel =
		floor( min( max( options.bottomLevel || 6, topLevel ), 6 ) );

	return Array.from( Array( bottomLevel - topLevel + 1 ), ( v, k ) => {
		const hxTagName = `h${ k + topLevel }`;
		return elderSelector + hxTagName + classSelector;
	} ).join( ',' );
};

/**
 * @author JuthaDDA
 */
const getHxs = ( options:HxOptions ):NodeListOf<HTMLHeadingElement> => {
	const root = options.root || document;
	return root.querySelectorAll( getHxSelector( options ) );
};

JavaScript

headings.js
/**
 * @author JuthaDDA
 * @param {Object} [options]
 * @param {string} [options.elderSelector]
 *     - ex. '.post', 'section >', 'hr +', '.meta ~'.
 *         White space will be added after this selector.
 * @param  {string} [options.classSelector]
 *     - ex. '.heading--red', ':hover', ':not(:first-child)'.
 *         White space before this selector will be trimmed
 *         to avoid being interpreted as a nested selector.
 * @param {number} [options.topLevel]
 *     - will be corrected to an integer in the range 1-6
 *         (ex. 0 to 1, 42 to 6, 1.9 to 1).
 * @param {number} [options.bottomLevel]
 *     - will be corrected to an integer in the range from `topLevel` to 6.
 * @return {string}
 */
const getHxSelector = ( options = {} ) => {
	const { min, max, floor } = Math;
	const elderSelector = options.elderSelector ?
		`${ options.elderSelector } ` : '';
	const classSelector = options.classSelector ?
		options.classSelector.trim() : '';
	const topLevel = floor( min( max( options.topLevel || 1, 1 ), 6 ) );
	const bottomLevel =
		floor( min( max( options.bottomLevel || 6, topLevel ), 6 ) );

	return Array.from( Array( bottomLevel - topLevel + 1 ), ( v, k ) => {
		const hxTagName = `h${ k + topLevel }`;
		return elderSelector + hxTagName + classSelector;
	} ).join( ',' );
};

/**
 * @author JuthaDDA
 * @see getHxSelector for further about `options`.
 * @param {Object} [options]
 * @param {Element|Document} [options.root]
 * @param {string} [options.elderSelector]
 * @param {string} [options.classSelector]
 * @param {number} [options.topLevel]
 * @param {number} [options.bottomLevel]
 * @return {NodeList}
 */
const getHxs = ( options = {} ) => {
	const root = options.root || document;
	return root.querySelectorAll( getHxSelector( options ) );
};

説明

getHxSelector()

第 1 引数 options のプロパティは,すべてオプショナルです.

options.elderSelector は,各 hx の前に附くセレクターです.子孫結合子のほか,子結合子,一般兄弟結合子,隣接兄弟接合子も使えます.後ろに半角スペースが挿入されるので,子孫結合子の半角スペースは省略可能です.

options.classSelector は,各 hx の後に附くセレクターです.クラス・セレクター,擬似クラス・セレクターが使えます.半角スペースが頭に含まれると子孫結合子として解釈されるので,.trim() されます.

options.topLeveloptions.bottomLevel で取得する見出しレベルの範囲を指定できます.たとえば,{ topLevel: 2, bottomLevel: 4 } と指定した場合は,h2, h3, h4 が対象となり,h1, h5, h6 は対象外となります.

Array.from( Array( n ), ( v, k ) => k ) MDN は,JavaScript でライブラリを使用せず 0 から n-1 の配列を作る方法の 1 つです1.第 2 引数のコールバック関数を書き換えることで,lodash.range( n ).map( ( n ) => someFunc( n ) ) Lodash 的なことをライブラリなしのメソッド 1 つで書けるので,覚えておいて損はないかと思います.

戻り値は string です.

getHxs()

options.root は,document が既定で,Element を指定することで適用範囲を限定できます.その他の options のプロパティは,そのまま getHxSelector() に渡され,getHxSelector() から戻ってきたセレクターに一致する見出し要素の NodeListgetHxs() の戻り値になります2

あとがき

前回の記事で予告した “ タグのレベルを一括して変更する関数” で今回の関数を使っていることに気付いたので,べつの記事にしたほうがいいかなと思い,こちらを先に投稿しました.

以上です.

  1. Cf: jsでrange関数をつくる - Qiita

  2. TypeScript での戻り値の型指定が NodeListOf<Element> じゃなくて NodeListOf<HTMLHeadingElement> でエラーが出ないのが,ちょっと不思議です.

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?