かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

CSS text-indentなコンテンツ内にinline-blockな要素がると崩れる

inline-block な要素は text-indent の値を継承するので、text-indent が設定されたコンテンツ内に fontawesome のアイコンとか inline-block な要素があると意図しない表示崩れを起こすことがあります。(fontawesome のアイコンは display: inline-block)

HTML

<ul class="icon-style-list">
  <li>
    <i class="fas fa-check-square list-icon"></i>
    アイカツ!を見ると幸せになれる
    <a href="#">
      <i class="fas fa-link"></i>
      LINK TEXT
    </a>
  </li>
</ul>

CSS

.icon-style-list {
  icon-style: none;
  text-indent: -1.5em;
  padding-left: 1.5em;
}
.list-icon {
  text-align: center;
  width: 1.5em;
}

👇 アイコンが inline-block なので親要素の text-indent が引き継がれて表示崩れてしまう text-indent の設定されたコンテナ内に inline-block のある場合

text-indent: 0 又は、display: inline; を設定する

text-indent の設定された要素の中にある inline-block な要素には text-indent: 0 又は、text-indent が引き継がれない display: inline を設定すればOK
個人的には inline-block のまま text-indent: 0 にしてしまう方が width なども効くので扱いやすいかなと思います。

HTML

<ul class="icon-style-list">
  <li>
    <i class="fas fa-check-square list-icon"></i>
    アイカツ!を見ると幸せになれる
    <a href="#">
      <i class="fas fa-link inline"></i>
      LINK TEXT
    </a>
  </li>
</ul>

CSS

.icon-style-list {
  icon-style: none;
  text-indent: -1.5em;
  padding-left: 1.5em;
}
.list-icon {
  text-indent: 0;
  text-align: center;
  width: 1.5em;
}
.inline {
  display: inline;
}

👇 inline-block の崩れを抑制

単純なアイコンだけどうにかする設定にしておきたいなら属性セレクタで丸っと設定しておくのが楽だと思います。

.icon-style-list {
  icon-style: none;
  text-indent: -1.5em;
  padding-left: 1.5em;
  
  [class*="fa-"] {
    text-indent: 0;
  }
}

example

See the Pen CSS inline-block inside a text-indent container by KIKIKI (@chaika-design) on CodePen.

 
CSSは全部 global だし、思わぬ所にバグ有りなので難解。


[参考]

#11 control(未来)

#11 control(未来)

  • 発売日: 2015/12/18
  • メディア: Prime Video

prime になったから見返してるけど C 面白いよね

JavaScript 数値かどうか判定したい

JavaScript には isNumber というメソッドなく、整数かどうか判定する Number.isInteger() と有限数かどうか判定する Number.isFinite(), isFinite() だけなので、数値かどうか判定したい時は独自実装するかライブラリをつかうしか無いのですが暗黙の型変換があるので意外とハマりどころがあります。( TypeScript 使え!ってのは無しで、

結論から書けば、typeofnumber なもので、NaNInfinity を除外すれば有限数な数値かどうか判定できる。

const isNumber = (n) => {
  return typeof(n) === 'number' && n - n === 0;
};

of

Number.isFinite(n);

暗黙の型変換のヤバイやつ

JavaScript で簡易的に数値に変換する方法として - 0 する方法があります。

"10" - 0; // => 10
"a" - 0; // => NaN

これを利用した数値かどうか判定するような関数

const isNumber = (n) => {
  const v = n - 0;
  if ( v || v === 0 ) {
    return true;
  }
  return false;
};

渡される値がある程度決まっている場合は問題ないのですが、特定の値の時意図しない動作になってしまう事があります

✅isNumber( 1 ); // true
✅isNumber( -1 ); // true
✅isNumber( 10.1 ); // true
✅isNumber( -1.1 ); // true
✅isNumber( 5e3 ); // true
✅isNumber( 10e+3 ); // true
✅isNumber( 10e-3 ); // true
✅isNumber( 0xff ); // true
✅isNumber( Math.PI ); // true
✅isNumber( parseInt('012') ); // true
✅isNumber( parseInt('012.5') ); // true
✅isNumber( Infinity ); // true
✅isNumber( -Infinity ); // true
✅isNumber( '10' ); // true
✅isNumber( '-10' ); // true

✅isNumber( NaN ); // false
⛔️isNumber( null ); // true
✅isNumber( undefined ); // false
⛔️isNumber( true ); // true
⛔️isNumber( false ); // true
⛔️isNumber( '' ); // true
⛔️isNumber( ' ' ); // true
✅isNumber( 'string' ); // false
⛔️isNumber( [] ); // true
⛔️isNumber( [1] ); // true
✅isNumber( {} ); // false
✅isNumber( {a: 1} ); // false
✅isNumber( function() {} ); // false

これは - 0 で数値化する際に暗黙の型変換で数値に変換されてしまう値があるためです

null - 0; //=> 0
true - 0; // => 1
false - 0; // => 0
"" - 0; // => 0
" " - 0; // => 0
[] - 0; // => 0
[1] - 0; //=> 1

数値かどうか判定する

数値でかつ NaN, Infinity は除外したい

コード

const isNumber = (n) => {
  if ( typeof(n) === 'number' && Number.isFinite(n) ) {
    return true;
  }
  return false;
};

or

const isNumber = (n) => {
  if ( typeof(n) === 'number' && n - n === 0 ) {
    return true;
  }
  return false;
};

👇 test

✅ isNumber( 0 ); // true
✅ isNumber( 1 ); // true
✅ isNumber( -1 ); // true
✅ isNumber( 10.1 ); // true
✅ isNumber( -1.1 ); // true
✅ isNumber( 5e3 ); // true
✅ isNumber( 10e+3 ); // true
✅ isNumber( 10e-3 ); // true
✅ isNumber( 0xff ); // true
✅ isNumber( Math.PI ); // true
✅ isNumber( parseInt('012') ); // true
✅ isNumber( parseFloat('012.5') ); // true

✅ isNumber( Infinity ); // false
✅ isNumber( -Infinity ); // false
✅ isNumber( NaN ); // false
✅ isNumber( null ); // false
✅ isNumber( undefined ); // false
✅ isNumber( true ); // false
✅ isNumber( false ); // false
✅ isNumber( "10" ); // false
✅ isNumber( "-10" ); // false
✅ isNumber( "" ); // false
✅ isNumber( " " ); // false
✅ isNumber( "string" ); // false
✅ isNumber( [] ); // false
✅ isNumber( [ 1 ] ); // false
✅ isNumber( {} ); // false
✅ isNumber( { a: 1 } ); // false
✅ isNumber( function() {} ); // false

文字列の数字も true にする

先の数値判定に文字列で数字になるものを true にする判定を追加すればOK

const isNumberAllowString = (n) => {
  const type = typeof(n);
  if ( type === 'number' && Number.isFinite(n) ) {
    return true;
  }
  if ( type === 'string' && n.trim() !== '' && Number.isFinite(n - 0) ) {
    return true;
  }
  return false;
};

👇 test

✅ isNumberAllowString( 0 ); // true
✅ isNumberAllowString( 1 ); // true
✅ isNumberAllowString( -1 ); // true
✅ isNumberAllowString( 10.1 ); // true
✅ isNumberAllowString( -1.1 ); // true
✅ isNumberAllowString( 5e3 ); // true
✅ isNumberAllowString( 10e+3 ); // true
✅ isNumberAllowString( 10e-3 ); // true
✅ isNumberAllowString( 0xff ); // true
✅ isNumberAllowString( Math.PI ); // true
✅ isNumberAllowString( parseInt('012') ); // true
✅ isNumberAllowString( parseFloat('012.5') ); // true
✅ isNumberAllowString( "10" ); // false
✅ isNumberAllowString( "-10" ); // false

✅ isNumberAllowString( Infinity ); // false
✅ isNumberAllowString( -Infinity ); // false
✅ isNumberAllowString( NaN ); // false
✅ isNumberAllowString( null ); // false
✅ isNumberAllowString( undefined ); // false
✅ isNumberAllowString( true ); // false
✅ isNumberAllowString( false ); // false
✅ isNumberAllowString( "" ); // false
✅ isNumberAllowString( " " ); // false
✅ isNumberAllowString( "string" ); // false
✅ isNumberAllowString( [] ); // false
✅ isNumberAllowString( [ 1 ] ); // false
✅ isNumberAllowString( {} ); // false
✅ isNumberAllowString( { a: 1 } ); // false
✅ isNumberAllowString( function() {} ); // false

解説的なもの

typeof で判定

typeof()number がになるもので判定すると先の暗黙の変換で数値になるものを除外することができる

typeof( null ); // => "object"
typeof( true ); // => "boolean"
typeof( false ); // => "boolean"
typeof( "" ); // => "string"
typeof( " " ); // => "string"
typeof( [] ); // => "object"
typeof( [1] ); // => "object"
NaN, Infinity を除外する

NaN, Infinitynumber なので別の条件で除外する

isFinite

isFinite
渡された値が有限数かどうかを判定します。
cf. isFinite() - JavaScript | MDN

JavaScript には isFiniteNumber.isFinite がある。

  • isFinite … 暗黙の型変換が行われる
  • Number.isFinite … 暗黙の型変換を行わない
    IEでは使えない cf. Number: isFinite | Can I use

e.g.

Number.isFinite( Infinity ); // => false
Number.isFinite( -Infinity ); // => false
Number.isFinite( NaN ); // => false
同値の引き算を利用する

同値で引き算をすると有効数だと 0 になるが、Infinity, NaNNaN になることを利用して除外する

Infinity - Infinity; // => NaN
-Infinity - (-Infinity); // => NaN
NaN - NaN ; // => NaN
文字列の数字を判定

string - 0 で数値化して、 isFinite()NaNInfinity になるものを弾く

"a" - 0; // => NaN
"Infinity" - 0; // => Infinity
"-Infinity" - 0; // => Infinity

ただし空文字列だけは 0 になってしまうので別途除外する必要がある

"" - 0; // => 0;
"   " - 0; // => 0;

"".trim(); // => ""
"   ".trim(); // => ""

所感

文字列の数字も許可したい場合は、is-number というライブラリを使うのが楽だと思います。


[参考]

入門Node.jsプログラミング

入門Node.jsプログラミング

  • 作者:Jonathan Wexler
  • 出版社/メーカー: 翔泳社
  • 発売日: 2019/09/25
  • メディア: 単行本(ソフトカバー)

JavaScript 分割代入な引数にデフォルト値を設定したい

オブジェクトを分割代入で引数にとる関数の引数にデフォルト値を設定する方法のメモ

引数を分割代入でとる関数

const greet = ({ name, msg }) => {
  console.log(`${msg}, ${name}!`);
};

greet({
  name: 'Ichigo',
  mgs: 'Hi',
});
// => "Hi, Ichigo!"

分割代入で引数を取ると、渡す引数の順番を考慮しなくて済むので便利です。
こんな関数の引数にデフォルト値を設定したい

分割代入な引数にデフォルト値を設定する

const greet = ({ name, msg = 'Hi' } = {}) => {
  console.log(`${msg}, ${name}!`);
};

greet({
  name: 'Aoi'
});
// => "Hi, Aoi!"

{key = default value} = {} の形でデフォルト値を設定できる
= {} を付けないと、引数を渡さずに関数を実行するとエラーになる
🙅‍♀️

const func = ({ foo = 'bar' }) => {
  console.log(foo);
};
func({foo: 'buzz'});
// => "buzz"
func();
// => TypeError: Cannot destructure property `foo` of 'undefined' or 'null'.

仕組みについて考えてみる

オブジェクトキーの省略形

ES2015 (ES6) でオブジェクトの key と同じ名前の変数名が value の時 : value を省略して書くことができる
obj = { prop: prop }obj = { prop } と書くことができる。

つまり、引数のデフォルト値になっている部分の

{ foo = 'bar' }
// 👇 の省略形
{ foo: foo = 'bar' }

オブジェクトの分割代入

分割代入される側 (変数になる側) のキーが渡されるオブジェクトの同じキーの値を参照している

const obj = { foo: 1, bar: 2 };
const { foo, bar, buzz } = obj;
// foo => 1, bar => 2, buzz => undefined
// 👇
foo = obj.foo; // => 1
bar = obj.bar; // => 2
buzz = obj.buzz; // => undefined

参照する値が undefined だと代入されない

const { foo = 'foo' } = { bar: 2 };
// foo => "foo"

{ foo = 'foo' }{ foo: foo = 'foo'} なので { foo = 'foo' } = { bar: 2 }

const obj = { bar: 2 };
const { foo: foo = 'foo' } = obj;
// foo => "foo"
// 👇
foo = obj.foo || 'foo' /* foo の value */ ;
// foo => "foo"

分割代入な引数のデフォルト値の仕組み

上記の挙動から変換すると次のようなイメージ

const greet = ({ name, msg = 'Hi' } = {}) => {
  console.log(`${msg}, ${name}!`);
};

greet({ name: 'Ichigo' });
// => "Hi, Ichigo!"

// 👇 挙動のイメージ

greet = ( { name, msg = 'Hi' } = {} ) => {}
// 👇関数の呼び出し greet({ name: 'Ichigo' });
greet = ( { name, msg = 'Hi' } = { name: 'Ichigo' } ) => {}
// 👇引数部分
var arg{ name, msg = 'Hi' } = obj{ name: 'Ichigo' };
name = obj.name || arg.name; // => "Ichigo"
msg  = obj.msg || arg.msg; // => 'Hi'
// 👇値が設定された変数が関数本体に渡される
(name="Ichigo", msg="Hi") => {
  console.log(`${msg}, ${name}!`);
}
// => "Hi, Ichigo!"

オブジェクトの分割代入はキーが変数名になるわけではない

const { foo: a = "bar" } = { foo: 1  };
foo; // => ReferenceError: foo is not defined
a; // => 1

// 👇
arg{ foo: a = "bar" } = obj{ foo: 1 }
a = obj[ 'foo' /* arg a のキー  */ ] || "bar"

つまり、{ foo } = { foo: 1 }のような分割代入される側のキーの名前が変数になるのではなく、省略されている変数が変数として出てきているという事だった。

const obj = { foo: 1 };
const { foo } = obj;
// 👇
arg{ foo: foo = undefined } = obj{ foo: 1 }
// 👇
foo(argの値の変数) = obj[ 'foo' /* arg の value foo のキーの "foo" */ ] || undefined;
foo; // => 1

異なる名前の変数に代入して既定値を設定する
プロパティに対して、1) オブジェクトから取り出して異なる名前の変数に代入することと、2) 取り出した値が undefined である場合に既定値を代入することの両方が可能です。

var {a: aa = 10, b: bb = 5} = {a: 3};
console.log(aa); // 3
console.log(bb); // 5
cf. 分割代入 - JavaScript | MDN

感想

オブジェクトの分割代入はキーが同じ名前の変数になっているのだと思っていたので、ミス理解を正せたので良かった。
理屈が解れば難しい構文ではなくなるので、基礎理解は大切ですね…
(基礎練習ばかりで試合に出たことがない人みたいになってきてるけど…)


[参考]

AIKATSU! ANION "NOT ODAYAKA" Remix

AIKATSU! ANION "NOT ODAYAKA" Remix

  • 発売日: 2019/03/30
  • メディア: MP3 ダウンロード

アイカツ!を見ると幸せになれる