HTML DOM Bindings for the Functional Extensions for Javascript
Clone or download
Latest commit f3ee6f2 Dec 28, 2018
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
LICENSE Initial commit Sep 22, 2018
README.md init Dec 16, 2018
fx.js init Dec 16, 2018
fxjs-dom.js init Dec 28, 2018
index.js init Sep 22, 2018

README.md

FxJS-DOM

HTML DOM Bindings for the FxJS

Selector

<div class="container div1" active="true">
  <ul class="list1">
    <li class="item1">1</li>
    <li class="item2">2</li>
    <li class="item3">3</li>
  </ul>
  <div class="div2" active="true">
    <ul class="list2">
      <li class="item4">4</li>
      <li class="item5">5</li>
    </ul>
  </div>
</div>

$, $.all

$document.querySelector를 사용하고 $.alldocument.querySelectorAll을 사용합니다.

console.log($('.container li'));
// li.item1

console.log($.all('.container li'));
// NodeList(5) [li.item1, li.item2, li.item3, li.item4, li.item5]

$.find, $.findAll

$.findel.querySelector를 사용하고 $.findAllel.querySelectorAll을 사용합니다.

console.log($.find('li', $('.container div')));
// li.item4

console.log($.findAll('li', $('.container div')));
// NodeList(2) [li.item4, li.item5]

console.log($.find('li', $('.container')));
// li.item1

console.log($.findAll('li', $('.container')));
// NodeList(5) [li.item1, li.item2, li.item3, li.item4, li.item5]

console.log($.findAll('ul li', $('.container')));
// NodeList(5) [li.item1, li.item2, li.item3, li.item4, li.item5]

el.querySelectorel.querySelectorAll의 아쉬운점

el.querySelectorel.querySelectorAll의 경우는 셀렉터의 시작으로 >를 사용할 수 없습니다.

try {
  document.querySelector('.container').querySelectorAll('> ul li');
} catch (e) {
  console.log(e);
  // DOMException: Failed to execute 'querySelectorAll' on 'Element': '> ul li' is not a valid selector.
}

el.querySelectorel.querySelectorAll의 경우는 셀렉터의 시작이 부모도 포함하고, 자식요소도 포함한다는 점을 유의해야합니다.

console.log(
  document
    .querySelector('.container')
    .querySelectorAll('[active=true] > ul li')
);
// NodeList(5) [li.item1, li.item2, li.item3, li.item4, li.item5]

with >, with &

아래 예제는 $.find, $.findAllel.querySelectorel.querySelectorAll의 차이를 보여줍니다.

  • $.find, $.findAll>를 셀렉터의 시작으로 사용할 수 있습니다.
  • &를 통해 부모 element에 대해서만 추가 조건을 붙일 수 있습니다.
console.log($.findAll('> ul li', $('.container')));
// NodeList(5) [li.item1, li.item2, li.item3]

console.log($.findAll('&[active="true"] li', $('.container')));
// NodeList(5) [li.item1, li.item2, li.item3, li.item4, li.item5]

console.log($.findAll('&[active="true"] > ul li', $('.container')));
// NodeList(5) [li.item1, li.item2, li.item3]

console.log($.findAll('&[active="false"] li', $('.container')));
// NodeList() []

$.closest

자신을 포함하여 셀렉터와 매칭되는 부모 엘리먼트를 찾습니다.

console.log($.closest('li', $('.item4')));
// li.item4

console.log($.closest('ul', $('.item4')));
// ul.list2

console.log($.closest('div', $('.item4')));
// div.div2

console.log($.closest('div.container', $('.item4')));
// div.container.div1

$.children

$.prevAll

$.nextAll

$.prev

$.next

$.is

첫 번째 인자에 전달된 셀렉터와 매칭이 되는지 확인합니다.

console.log($.is('.item1', $('li:nth-child(1)')));
// true

console.log($.is('.item1', $('li:nth-child(2)')));
// true

$.contains

Create

$.els

console.log($.els('<span class="s1">1</span>'));
// HTMLCollection(2) [span.s1]

console.log($.els('<span class="s1">1</span><span class="s2">2</span>'));
// HTMLCollection(2) [span.s1, span.s2]

$.el

console.log($.el('<span class="s1">1</span>'));
// span.s1

console.log($.el('<span class="s1">1</span><span class="s2">2</span>'));
// span.s1

Manipulation

$.append

$.append($('.comments'), $.el('<div class="comment">새 댓글</div>'));

$.prepend

$.prepend($('.posts'), $.el('<div class="post">새 글</div>'));

$.before

$.after

$.remove

$.remove($('.post'));

$.text

console.log($.text($.el('<div>hi</div>')));
// "hi"

$.setText

console.log($.setText('ho', $.el('<div></div>')));
// HTMLDivElement <div>ho</div>

$.html

console.log($.html($.el('<div><span>hi</span></div>')));
// "<span>hi</span>"

$.setHtml

console.log($.setHtml('<span>ho</span>', $.el('<div></div>')));
// HTMLDivElement <div><span>ho</span></div>

$.outerHTML

console.log($.outerHTML($.el('<div><span>hi</span></div>')));
// "<div><span>hi</span></div>"

$.setOuterHTML

let el = $('#div1');
$.setOuterHTML('<div id="div1" class="hi2"></div>', el);
console.log($('#div1'));
// HTMLDivElement <div id="div1" class="hi2"></div>

$.val

console.log($.val($.el('<input type="text" value="hoho">')));
// "hoho"

$.setVal

console.log($.setVal('hoho', $.el('<input type="text">')).value);
// "hoho"

$.attr

console.log($.attr('type', $.el('<input type="text" value="hoho">')));
// "text"

$.setAttr

console.log($.setAttr({ status: 'ho' }, $.el('<div status="hi">')));
// HTMLDivElement <div status="ho"></div>
console.log($.setAttr({ status: 'ho', class: 'ye' }, $.el('<div status="hi">')));
// HTMLDivElement <div status="ho" class="ye"></div>
console.log($.setAttr(['status', 'ho'], $.el('<div status="hi">')));
// HTMLDivElement <div status="ho"></div>
console.log($.setAttr({ status: '' }, $.el('<div status="hi">')));
// HTMLDivElement <div status></div>

$.removeAttr

console.log($.removeAttr('status', $.el('<div status="hi">')));
// HTMLDivElement <div></div>

CSS

$.addClass

console.log($.addClass('selected', $.el('div')));
// HTMLDivElement <div class="selected"></div>
console.log($.addClass('hi ho', $.el('div')));
// HTMLDivElement <div class="hi ho"></div>
console.log($.addClass('hi', $.el('<div class="ye">')));
// HTMLDivElement <div class="ye hi"></div>

$.removeClass

console.log($.removeClass('selected', $.el('<div class="selected"></div>')));
// HTMLDivElement <div class></div>
console.log($.removeClass('hi ho', $.el('<div class="hi ho"></div>')));
// HTMLDivElement <div class></div>
console.log($.removeClass('hi', $.el('<div class="ye hi">')));
// HTMLDivElement <div class="ye"></div>

$.removeClass

console.log($.removeClass('selected', $.el('<div class="selected"></div>')));
// HTMLDivElement <div class></div>
console.log($.removeClass('hi ho', $.el('<div class="hi ho"></div>')));
// HTMLDivElement <div class></div>
console.log($.removeClass('hi', $.el('<div class="ye hi">')));
// HTMLDivElement <div class="ye"></div>

$.toggleClass

console.log($.toggleClass('selected', $.el('<div class="selected"></div>')));
// HTMLDivElement <div class></div>

console.log($.toggleClass('selected', $.el('<div></div>')));
// HTMLDivElement <div class="selected"></div>

$.css

$.setCss

$.show

$.hide

$.toggle

$.offset

console.log(
  $.offset(
    $.append(
      $('body'),
      $.setCss(
        { position: 'absolute', top: '20px', left: '30px', 'margin-top': '50px' },
        $.el('div')))));
// { top: 70, left: 30 }

$.width

width

$.height

height

$.innerWidth

width + paddingLeft + paddingRight + borderLeft + borderRight

$.innerHeight

height + paddingTop + paddingBottom + borderTop + borderBottom

$.outerWidth

innerWidth + marginLeft + marginRight

$.outerHeight

innerHeight + marginTop + marginBottom

$.scrollTop

$.scrollLeft

$.setScrollTop

$.setScrollLeft

Event

$.on

$.onel.addEventListener를 대신합니다. $.on은 이벤트를 등록할 함수를 리턴하며, 커링 방식으로만 사용할 수 있습니다.

  • 인자로 받은 함수를 조작하지 않고 el.addEventListener에 그대로 적용하여, 같은 엘리먼트에 같은 이벤트와 같은 함수를 등록이 되지 않는 el.addEventListener의 특징을 그대로 유지했습니다.
  • el.addEventListenercapture, passive 등의 옵션을 사용할 수 있습니다.
  • e.preventDefault, e.stopPropagation을 사용할 수 있습니다.
  • el.removeEventListener$.off를 사용할 수 있습니다.
<button type="button" id="btn1">
  <span>btn1</span>
</button>
const addClickEvent = $.on('click', function(e) {
  console.log(e.currentTarget); // #btn1
  console.log(e.target); // span
});

addClickEvent($('#btn1'));
addClickEvent($('#btn1')); // 두 번 등록해도 추가로 등록되지 않음.
$.trigger('click', $('#btn1 span'));
// #btn1
// span

$.on의 두 번째 인자에 셀렉터를 전달하면 매칭되는 자식요소에 이벤트를 등록합니다. 이 방식은 위임 방식이 아니며, 역시 el.addEventListener의 주요 특징과 기능을 모두 사용할 수 있습니다.

<div class="articles">
  <div class="article">
    <button type="button" class="remove"><span>삭제</span></button>
  </div>
  <div class="article">
    <button type="button" class="remove"><span>삭제</span></button>
  </div>
</div>
const Articles = {
  addEvents: pipe(
    $.on('click', '.article:nth-child(1)', function(e) {
      console.log(e.currentTarget);
    }),
    $.on('click', '.article', function(e) {
      console.log(e.currentTarget);
    }, { capture: true }),
    $.on('click', '.remove', function(e) {
      console.log(e.currentTarget);
    }))
};

Articles.addEvents($('.articles'));

$.trigger('click', $('.articles .article:nth-child(1) .remove')); // 한 번만 실행
// button.remove
// div.article

$.trigger('click', $('.articles .article:nth-child(2) .remove'));
// div.article
// button.remove

$.append($('.articles'), $.el(`
  <div class="article new">
    <button type="button" class="remove"><span>삭제</span></button>
  </div>
`));

Articles.addEvents($('.articles'));

$.trigger('click', $('.articles .article:nth-child(1) .remove')); // 한 번만 실행
// button.remove
// div.article

$.trigger('click', $('.articles .article:nth-child(3) .remove'));
// button.remove
// div.article.new

$.off

$.on에 전달한 모든 인자를 동일하게 전달하여 이벤트를 지울 수 있습니다.

<button type="button" id="btn2"></button>
const eventArgs = ['click', function() { console.log('hi~') }];
$.on(...eventArgs)($('#btn2'));
$.off(...eventArgs)($('#btn2'));
$.trigger('click', $('#btn2'));
// nothing

$.delegate

이벤트 위임 방식으로 이벤트를 등록합니다. 이벤트를 등록하고자 하는 엘리먼트가 동적으로 간편하게 이벤트를 등록해둘 수 있습니다.

<div class="users">
</div>
go(
  $('.users'),
  $.delegate('click', '.remove', function() {
    console.log('remove user');
  }));

$.append($('.users'), $.el(`
  <div class="user new">
    <button type="button" class="remove"><span>삭제</span></button>
  </div>
`));

$.trigger('click', $('.users .remove'));
// remove user

이벤트 위임 방식은 아래와 같은 옵션들을 사용할 필요가 없는 상황에 한하여 사용하는 것을 권장합니다.

  • el.addEventListenercapture, passive 등의 옵션을 사용할 수 없습니다.
  • e.preventDefault, e.stopPropagation을 사용할 수 없습니다.
  • el.removeEventListener$.off를 사용할 수 없습니다.
  • mouseleave, mouseenter는 정상 동작하지 않습니다.

don.js에서는 이벤트 위임 방식에서 위 기능들을 모두 구현하여 제공했지만, 사실상 브라우저의 이벤트에 대한 모든 동작을 라이브러리 위에 다시 구현하는 장황한 코드들이 필요하며, 경험상 그 실용성이 떨어진다고 생각하여 FxJS-DOM은 해당 기능을 구현하지 않는 컨셉으로 가고자 합니다.

$.delegate + & 응용

<div class="signup" agree="false">
  <input type="checkbox">
  <button type="button">가입</button>
</div>
go($('.signup'),
  $.delegate('change', 'input', function(e) {
    $.setAttr({ agree: e.currentTarget.checked }, e.delegateTarget);
  }),
  $.delegate('click', '&[agree="false"] button', function() {
    console.log('동의해주세요!');
  }),
  $.delegate('click', '&[agree="true"] button', function() {
    console.log('감사합니다!');
  }));

$.trigger('click', $('.signup button'));
// 동의해주세요!
$.trigger('click', $('.signup input'));
$.trigger('click', $('.signup button'));
// 감사합니다!

Data

$.data, $.setData

<div class="item" fxd-data='{"id": 1, "active": true}'></div>
const { id, active } = $.data($('.item'));
console.log(id, active);
// 1 true

const data = $.data($.setData({ id: 1, active: false }, $('.item')));
console.log(data.active);
// false
data.active = true;
console.log($.data($('.item')).active);
// true

Fetch

$.get, $.post, $.put, $.deleteContent-Typeapplication/json으로 설정되어있으며 응답이 오면 JSON 객체를 반환합니다. 4개 함수 모두 필요 인자는 2개 이상이며, 인자를 1개만 전달하면 함수를 리턴합니다.

$.get

$.get('/api/posts', { offset: 0, limit: 10 }); // GET '/api/posts?offset=0&limit10'
// Promise [{id: 1, ...}, {id: 2, ...}, ...]

$.post

$.post('/api/posts', { content: 'ho~'}); // POST /api/posts, BODY { content: 'ho~' }
// Promise {id:1, content: 'ho', created_at: ... }

$.put

$.put(`/api/posts/${post.id}`, post); // PUT /api/posts/1, BODY { id: 1, ... }
// Promise {id:1, content: 'ho', updated_at: ... }

$.delete

$.delete(`/api/posts/${post.id}`, undefined); // DELETE /api/posts/1