JavaScript Deep Dive-40章-2(アクティビティ)

49760 ワード

DOM要素のデフォルト動作の操作


DOM要素にはそれぞれ基本動作があります.たとえば、a要素をクリックするとhrefドキュメントツリーに指定されたリンクに移動し、チェックボックスまたは比要素をクリックすると選択または無効になります.

preventDefault();


イベントオブジェクトのpreventDefaultメソッドは、これらのDOM要素の基本的な操作を中断します.

document.querySelector('a').onclick = e => {
  e.preventDefault();
}

document.querySelector(input[type=checkbox]').onclick = e => {
           //checkbox 요소의 기본 동작을 중단한다.
           e.preventDefault();
		};

イベントの伝播を防止


イベントオブジェクトのstopPropagationメソッドは、イベントの伝播を停止します.次の例を示します.
<div class="container">
  <button class="btn1">Button 1</button>
  <button class="btn2">Button 2</button>
  <button class="btn3">Button 3</button>
</div>
//이벤트 위임. 클릭된 하위 버튼 요소의 color를 변경한다.
document.querySelector('.container').onclick({ target }) => {
  if(!target.matches('.container > button')) return;
};

// .btn2 요소는 이벤트를 전파하지 않으므로 상위 요소에서 이벤트를 캐치할 수 없다.
document.querySelector('.btn2').onclick = e => {
  e.stopPropagation(); // 이벤트 전파 중단
  e.target.style.color = 'blue';
};
上記の例では、上流DOM要素Container要素にアクティビティを委任しています.したがって、子DOM要素で発生するクリックイベントは、親DOM要素container要素によってスナップされ、処理されます.
しかし、サブエレメントではbtn 2エレメントがイベントを自分で処理する.このとき、btn 2要素は、自分が励起したイベントの伝播を停止し、自分にバインドされたイベントヘンドラーのみを実行します.
これにより、stopPropagationメソッドは、サブDOM要素内のイベントを個別に処理するために、イベントの伝播を中断する.
イベント階層の要素のサブ要素に個別のイベントを付与する場合は、stopPropagationを介して他の要素に伝播しないように設定します.

イベントハンドラ内部のthis


イベントハンドラツリー方式


次の例のhandleClick関数の内部にあるthisは、グローバルオブジェクトウィンドウを指します.
<body>
  <button onclick="handleClick()">Click me </button>
<body>
function handleCLick() {
  console.log(this); // window
}
イベントハンドラツリーの値として指定された文字列は、実際にはデフォルトで生成されたイベントハンドラの文です.したがって、handleClick関数は、イベントハンドラによって通常の関数として呼び出されます.
通常の関数として呼び出される関数の内部のthisは、グローバルオブジェクトを指します.したがって、handleClick関数の内部では、thisはグローバルオブジェクトウィンドウを指します.
ただし、イベントハンドラを呼び出す際にパラメータとして渡されるthisは、イベントをバインドするDOM要素を指す.
<body>
  <button onclick="handleClick(this)">Click me </button>
<body>
function handleClick(button) {
  console.log(button); // 이벤트를 바인딩한 button 요소
  console.log(this); // window
}
上記の例では、handleClick関数に渡されるthisは、デフォルトで生成されたイベントハンドラ内部のthisです.すなわち,イベントハンドラツリー方式によりデフォルトで生成されるイベントハンドラ内部のthisは,イベントをバインドするDOM要素である.
これはイベントハンドラの処理方法と同じです.

イベントハンドラ構成とaddEventListenerメソッド


イベントハンドラの構成とaddEventListenerメソッドは、イベントハンドラ内のこのイベントをバインドするDOM要素を指します.
すなわち,イベントハンドラ内部のthisはイベントオブジェクトのcurrentTargetpropertyと同じである.
<button class="btn1">0</button>
<button class="btn2">0</button>


const $button1 = document.querySelector('.btn1');
const $button2 = document.querySelector('.btn2');

// 이벤트 핸들러 프로퍼티 방식
$button1.onclick = function (e) {
console.log(this) // $button1
console.log(e.currentTarget) // $button1
console.log(this === e.currentTarget) // true
  ++this.textContent;
}

$button2.addEventListener('click', function(e){
  console.log(this); // $button2
  console.log(e.currentTarget); // $button2
  console.log(this === e.currentTarget); // true
  ++this.textContent;
})
矢印関数で定義されたイベントハンドラ内のthisは、親のthisを指します.
矢印関数には、関数自体のthisバインドはありません.

<button class="btn1">0</button>
<button class="btn2">0</button>


const $button1 = document.querySelector('.btn1');
const $button2 = document.querySelector('.btn2');

// 이벤트 핸들러 프로퍼티 방식
$button1.onclick = e => {
console.log(this) // window
console.log(e.currentTarget) // $button1
console.log(this === e.currentTarget) // false
  //this는 window를 가리키므로 window.textContent에 Nan을 할당한다
  //undefined + 1;
  ++this.textContent;
}

$button2.addEventListener('click', function(e){
  console.log(this); // $button2
  console.log(e.currentTarget); // $button2
  console.log(this === e.currentTarget); // true
  
  //this는 window를 가리키므로 window.textContent에 Nan을 할당한다.
  //undefined + 1;
  ++this.textContent;
});

クラスにイベントハンドラをバインドします。


次の例では、addEventListenerメソッドと同様に、イベントハンドラ構成メソッドを使用します.
<button class="btn">0</button>

class App {
  constructor() {
    this.$button = document.querySelector('.btn');
    this.count = 0;
    
    //increase 메서드를 이벤트 핸들러로 등록
    this.$button.onclick = this.increase;
  }
  
  increase() {
    //이벤트 핸들러 $increase 내부의 this는 DOM 요소(this.$button)을 가리킨다.
    //따라서 this.$button은 this.$button.$button과 같다.
    this.$button.textContent = ++this.count;
    // Type Error : textContent of undefined
  }
}

new App();
上記の例のインクリメンタルメソッドでは、thisはクラスが作成するインスタンスを指しません.
イベントハンドラ内のthisは、イベントをバインドするDOM要素を指すため、インクリメンタルメソッド内のthisはthis.$矢印ボタン.したがって、インクリメンタルメソッドをイベントハンドラにバインドする場合は、bindメソッドを使用して、インクリメンタルメソッド内のthisがクラスが作成するインスタンスを指すように伝達する必要があります.
class App {
  constructor() {
    this.$button = document.querySelector('.btn');
    this.count = 0;
    
    //increase 메서드를 이벤트 핸들러로 등록.
    // this.$button.onclick = this.increase;
    
    // increase 메서드 내부의 this가 인스턴스를 가리키도록 한다. 
    this.$button.onclick = this.increase.bind(this);
  }
  
  increase() {
    this.$button.textContent = ++this.count;
  }
}

new App();
あるいは、クラスフィールドに割り当てられた矢印関数をイベントハンドラとして登録して、イベントハンドラ内部のthisがインスタンスを指すようにすることもできます.
ただし、イベントハンドラのインクリメンタルは、プロトタイプメソッドではなくインスタンスメソッドとなります.
<button class="btn">0</button>

class App {
  constructor() {
    this.$button = document.querySelector('.btn');
    this.count = 0;
    
    // 화살표 함수인 increase를 이벤트 핸들러로 등록
    this.$button.onclick = this.increase;
  }
  
  // 클래스 필드 정의
  // increase는 인스턴스 메서드이며 내부의 this는 인스턴스를 가리킨다.
  increase = () => this.$button.textContent = ++this.count;
}
new App();

イベントハンドラにパラメータを渡す


関数にパラメータを渡すには、関数を呼び出すときにパラメータを渡す必要があります.
イベントハンドラツリーメソッドでは、関数呼び出し文を使用してパラメータを渡すことができますが、イベントハンドラメソッドとaddEventListenerメソッドでは、ブラウザがイベントハンドラを呼び出すので、関数呼び出し文ではなく関数自体を登録する必要があります.
したがって、パラメータを渡すことはできません.
しかし買収が全く伝わらないわけではない.
次の例に示すように、イベントハンドラ内で関数を呼び出すことでパラメータを渡すことができます.
<label> User name <input type='text'></label>
  <em class="message"></em>
  
  const MIN_USER_NAME_LENGTH = 5; // 이름의 최소 길이
  const $input = document.querySelector('input[type=text]');
  const $msg = document.querySelector('.message');
  
  const checkUserNameLength = min => {
    $msg.textContent
      = $input.value.length < min
      ? ` 이름은 ${min}자 이상 입력해주세요 `
      : '';
    };
  
  // 이벤트 핸들러 내부에서 함수를 호출하면서 인수를 전달한다.
  $input.onblur = () => {
    checkUserNameLength(Min_USER_NAME_LENGTH);
    };
あるいは、パラメータは、イベントを返すヘンドラーの関数を呼び出すことによって伝達することができる.
const checkUserNAmeLength = min = e => {
$msg.textContent
    = $input.value.length < min ? ` 이름은 ${min}자 이상 입력해주세요 ` : '';
}

$input.blur = checkUserNameLength(MIN_USER_NAME_LENGTH);

カスタムイベント


イベントオブジェクトの継承構造で見たように、イベントオブジェクトはイベント、UIイベント、MouseEventなどのイベントジェネレータ関数によって作成できます.
イベントが発生すると、デフォルトで生成されたイベントオブジェクトは、イベントのタイプに応じてイベントタイプを決定します.ただし、イベント、UIイベント、MouseEventなどのイベントジェネレータ関数を呼び出して明示的に作成したイベントオブジェクトは、任意のタイプのイベントを指定できます.このような開発者の意図で生成されたイベントをCustomイベントと呼ぶ.
イベントジェネレータ関数は、イベントタイプを表す文字列を最初のパラメータとして渡します.イベント・タイプを表す文字列は、既存のイベント・タイプを使用するか、既存でないイベント・タイプを使用して新しいイベント・タイプを指定できます.この場合、customEventイベントジェネレータ関数は通常使用されます.
// keyboardEvent 생성자 함수로 keyup 이벤트 타입의 커스텀 이벤트 객체를 생성
const keyboardEvent = new keyboardEvent('keyup');
console.log(keyboardEvent.type); // keyup


//CustomEvent 생성자 함수로 foo 이벤트 타입의 커스텀 이벤트 객체를 생성
const customEvent = new customEvent('foo');
console.log(customEvent.type); // foo
作成したカスタムイベントオブジェクトはバッファリングされず、preventDefaultメソッドにキャンセルされません.
すなわち、「customイベント」オブジェクトの「バブル」および「輪郭解除可能」の値はfalseにデフォルト設定されます.
const customEvent = new MouseEvent('click');
console.log(customEvent.type) // click;
console.log(customEvent.bubbles) // false;
console.log(customEvent.cancleable) // false;
customイベントオブジェクトのバブルまたはキャンセル可能なプロファイルをtrueに設定すると、イベント作成者関数の2つのフェースパラメータがバブル、キャンセル可能なプロファイルを持つオブジェクトに渡されます.
const customEvent = new MouseEvent('click',{
  bubbles: true,
  cancelable: true
});
カスタムイベントオブジェクトの場合、バルーン、構成解除項目、およびイベントタイプに応じてこのベイト固有の構成値を指定できます.
MouseEventビルダー関数を使用して作成したマウスイベントオブジェクトは、次のとおりです.
マウスポインタ座標情報を表すマウスイベントオブジェクトの一意の輪郭.
ボタン情報を表すプロパティがあります.
これらのイベントオブジェクト固有のproperty値を指定するには、propertyをイベントジェネレータ関数の2番目の引数として渡します.
const mouseEvent = new MouseEvent('click',{
  clientX: 50,
  clientY: 100
});


// KeyboardEvent 생성자 함수로 keyup 이벤트 타입의 커스텀 이벤트 객체를 생성
const KeyboardEvent = new KeyboardEvent('keyup', { key: 'Enter' });

console.log(keyboardEvent.key) // Enter
イベント作成者関数によって生成されるデータムイベントで、istrusted propertyの値は常にfalseです.
カスタムイベントではなくユーザーの動作によって生成されるイベントオブジェクトのistrusted property値は常にtrueです.
const customEvent = new InputEvent('foo');
console.log(customEvent.isTrusted); // false

カスタムイベント派遣


生成されたカスタムイベントは、dispatchEventメソッドによってディスパッチすることができる(イベントの動作を励起する).
イベントオブジェクトをパラメータとしてDispatchEventメソッドに渡すと呼び出され、パラメータとして渡されるイベントタイプのイベントが励起されます.
<button class="btn">Click me </button>

const $button = document.querySelector('.btn');

// 버튼 요소에 foo 커스텀 이벤트 핸들러를 등록
// 커스텀 이벤트를 디스패치하기 이전에 이벤트 핸들러를 등록해야 한다.
$button.addEventListener('click', e => {
  console.log(e) // MouseEvent {isTrusted: false, screenX:0, ... }
  alert(`${e} Clicked!');
});

// 커스텀 이벤트 생성

const customEvent = new MouseEvent('click');

//커스텀 이벤트 디스패치(동기 처리), click 이벤트가 발생한다.
$button.despatchEvent(customEvent);
通常、イベントハンドラは非同期で実行されますが、イベントメッセージを割り当てるイベントハンドラは同期で呼び出されます.言い換えれば、DispatchEventメソッドを呼び出すことは、Customイベントにバインドされたイベントハンドラを直接呼び出すのと同じである.したがって、DispatchEventメソッドdispatchイベントを使用する前に、イベントハンドラを登録して基準イベントを処理する必要があります.
既存のイベントタイプではなく任意のイベントタイプを指定してイベントオブジェクトを作成する場合、通常はCustomEventイベントジェネレータ関数が使用されます.
const customEvent = new CustomEvent('foo');
console.log(customEvent.type) // foo
この場合、CustomEventイベント作成者関数は、2番目の引数として、イベントと転送したい情報を含む詳細なオブジェクトを渡すことができます.この情報は、イベントオブジェクトのdetail property(e.detail)に含まれます.
<button>Click me</button>

// 버튼 요소에 foo 커스텀 이벤트 핸들러를 등록
  // 커스텀 이벤트를 디스패치 하기 이전에 이벤트 핸들러에 등록해야 한다.
$button.addEventListener('foo', e => {
  // e.detail에는 CustomEvent 함수의 두 번째 인수로 전달한 정보가 담겨 있다.
  alert(e.detail.message);
});


const customEvent = new CustomEvent('foo', {
  detail: { message: 'Hello'} 
});

$button.dispatchEvent(customEvent);
カスタムイベントオブジェクトを作成するために既存でないイベントタイプを指定した場合は、addEventListenerメソッドでイベントハンドラを登録する必要があります.イベントハンドラツリー/構成を使用できないのは、「on+イベントタイプ」からなるイベントハンドラツリー/構成プログラムが要素ノードに存在しないためです.
たとえば、「foo」で任意のイベントタイプのカスタムイベントが作成された場合、「onfoo」と呼ばれるHandlerツリー/Propertyは要素ノードに存在しないため、イベントハンドラツリー/Propertyではイベントハンドラを登録できません.