見出しタグを .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
/**
* @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
/**
* @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.topLevel
と options.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()
から戻ってきたセレクターに一致する見出し要素の NodeList
が getHxs()
の戻り値になります2.
あとがき
前回の記事で予告した “ タグのレベルを一括して変更する関数” で今回の関数を使っていることに気付いたので,べつの記事にしたほうがいいかなと思い,こちらを先に投稿しました.
以上です.
-
TypeScript での戻り値の型指定が
NodeListOf<Element>
じゃなくてNodeListOf<HTMLHeadingElement>
でエラーが出ないのが,ちょっと不思議です. ↩