티스토리 뷰

728x90
SMALL

DOM 개요

문서 객체 모델(Document Object Model) 즉, DOM은 HTML과 XML 문서의 내용을 조작하고 나타내는 기반 API다. 

노드의 종류는 세가지이다.

계층 구조의 가장 위쪽에는 문서 전체를 나타내는 Document 노드가 있고, 아래쪽으로는 HTML 요소를 나타내는 Element 노드와 텍스트를 나타내는 Text 노드가 있다. Document, Element, Text는 Node 객체의 서브 클래스이며, 각각 레퍼런스 부분에 설명되어 있다.

Documnet 타입은 HTML 또는 XML 문서를 나타내며,  Element 클래스는 그 문서의 요소를 나타낸다.

반면 제네릭 타입의 서브 클래스읜 HTMLDocument와 HTMLElement는 좀더 구체적으로 HTML의 문서와 요소만을 말함

Comment는 주석.

Attr 노드는 XML이나 HTML의 속성을 나타내지만 거의 사용하지 않는다.

문서 요소 선택

대부분의 클라이언트 측 자바스크립트 프로그램은 하나 이상의 문서 요소에 어떤 작업을 한다. 이런 프로그램의 작동시, Document 객체를 참조할 땐 전역 변수 document를 사용하면 되지만, 각 요소를 수정하려면 해당 요소를 가리키는 Element 객체를 선택할 방법이 있어야 한다. DOM에는 요소를 선택하는 다양한 방법이 정의되어 있다.

ID로 요소 선택하기

한 문서안에서 id는 유일해야 한다. 가장 간단하고 일반적인 방법

var section1=document.getElementById('section1");

Name으로 요소 선택하기

HTML name 속성의 원래 목적은 폼 요소에 이름을 부여하는 것으로 폼 데이터를 서버로 전송할 때 사용한다.

name은 유일성 보장x. name 속성은 일부의 HTML 요소에서만 의미가 있다. 폼과 폼 안의 요소, <iframe>, <img> 요소

var radiobuttons=document.getElementsByName("favorite_color");

Type으로 요소 선택하기

var spans=document.getElementsByTagName("span");

var firstpara=document.getElementsByTagName("p")[0];

HTMLDocument 클래스에는 다른 노드에 손쉽게 접근하기 위한 몇 가지 프로퍼티가 정의되어 있다.

document.shipping_address 를 document.forms.shipping_address;; 로 더 명확하게 참조

HTMLDocument에는 요소 집합이 아닌 특정 단일 요소를 참조하는 두 개의 프로퍼티가 더 잇다.

document.body  document.head

* getElementByName(), getElementsByTagName()은 Nodelist 객체를 반환

  document.images, document.forms는 HTMLCollection 객체를 반환

CSS Class로 요소 선택하기

HTML 요소의 class 속성 값은 여러 개의 식별자가 공백으로 구분된 리스트다. 동일한 식별자를 class 속성에 포함하고 있는 모든 요소는 한 집합의 일부가 된다.

var warnings=document.getElementsByClassName("warning");

//이름이 log인 요소의 자손 중에서 class에 fatal과 error가 있는 요소를 모두 찾는다.
var log=document.getElementsByName("log");
var fatal=log.getElementsByClassName("fatal error");

CSS 선택자로 요소 선택하기

CSS 스타일시트에는 문서 내부의 요소나 요소 집합을 선택할 때 사용하는 선택자(selector)라는 매우 강력한 문법이 있다. 

CSS 선택자는 ID와 name 속성, HTML 태그와 class의 이름을 이용해서 요소를 선택할 수 있다. CSS3 선택자 표준을 따르는 W3C의 'Selectors API'라는 별도의 표준안에는, 주어진 선택자와 일치하는 요소를 가져오는 자바스크립트 메서드가 있다. Document 객체의 querySelectorAll() 메서드는 Selectos API의 핵심이다. querySelector()는 첫번째 요소

document.all[]

텍스트 노드를 제외한 문서 내 모든 요소를 나타내는 document.all[]

document.all[0]           //문서의 첫번째 요소
document.all["navbar"]    //id나 name 속성 값이 navbar인 요소 혹은 요소의 집합
document.all.navbar       //위와 같음
document.all.tags("div")  //문서 안의 모든 <div> 태그
document.all.tags("p")[0] //문서 안의 첫 <p> 태그

문서 구조와 탐색

Node 트리로서의 문서

Document 객체와 그 속의 Element 객체, 그리고 문서의 텍스트 변화를 나타내는 Text 객체 모두는 Node 객체다. Node 객체에는 다음과 같은 중요한 프로퍼티가 정의되어 있다.

parentNode : 상위 node 객체. 부모 객체가 없으면 null

childNodes : Nodelist이며, 한 노드의 자식 노드에 대한 실시간 변화를 반영

firstChild, lastChild : 한 노드의 자식 노드 중 첫번째 노드와 마지막 노드. 자식 노드가 없다면 null

nextSibling, previousSibling : 이전 혹은 다음의 형제 노드(같은 부모 노드를 가진 두 노드를 형제 노드라 함)

nodeType : 노드의 종류. Document 노드 9, Element 노드의 1. Text는 3, Comment는 8, DocumentFragment 11

nodeValue : Text 노드나 Comment 노드의 텍스트 내용

nodeName : 대문자로 바꾼 요소의 HTML 태그명

첫 자식 객체의 두번째 자식 객체는 <body>이다. 두번째 자식 객체는 <body>가 아닌 <head> 요소이다.

Element 트리로서의 문서

문서에서 주된 관심사가 요소 안의 텍스트나 그 사이의 공백이 아니라 Element 객체 자체일 때는, 문서를 Element 객체의 계층 구조로 다루는 API가 효과적이다. 게다가 이 api는 같은 구성 요소이긴 하지만 text와 comment 노드는 무시한다

이 API에서 처음 다룰 내용은 Element 객체의 children 프로퍼티이다. 이 프로퍼티는 childNodes처럼 NodeList를 반환하지만 childNodes와는 달리 Element 객체만 담고 있다. Text와 Comment 노드에는 자식 객체가 없다는 점을 생각할 때, 앞에서 설명한 Node.parentNode 프로퍼티는 절대나 Text나 Comment 노드를 반환하지 않는다는 뜻이다. 어떤 Element 객체의 parentNode는 언제나 다른 Element 객체이며, 계층 구조의 최상위는 Document 혹은 DocumentFragment 객체다.

요소 기반의 문서 탐색 API 중 두번째로 다룰 내용은 node 객체의 자식과 형제 프로퍼티를 구현한 Element 프로퍼티들이다.

firstElementChild, lastElementChild

firstChild와 lastChild 프로퍼티와 유사하지만 Element 자손 객체만 반환한다.

nextElementSibling, previousElementSibling

위와 같음

childElementCount

자손 객체의 수. childeren.length와 같은 값 반환

속성

HTML 요소는 태그의 이름과 속성(attributre)이라 부르는 이름/값 쌍들의 집합으로 구성된다.

element 객체의 프로퍼티인 HTML 속성

HTML 문서 요소를 나타내는 HTMLElement 객체에는 해당 요소의 HTML 속성에 대응하는 읽기/쓰기 프로퍼티가 있다. HTMLElement에는 idtitle lang,dir과 같은 범용 HTML 속성이 프로퍼티로 정의되어 있고, onclick 같은 이벤트 핸들러 프로퍼티도 존재한다.

HTML 속성은 대소문자를 구분하지 않지만, 자바스크립트 프로퍼티 이름은 대소문자를 구분한다. HTML 속성 이름을 자바스크립트의 프로퍼티로 변환하기 위해서 HTML 속성명은 소문자로 작성한다. HTML 속성의 값을 지정하는 프로퍼티 기반의 API는 HTML 요소에서 속성을 지우는 방법을 제공하지 않는다.

var f=document.forms[0];
f.action="http://www.example.com/submit.php";
f.method="POST";

Non-HTML 속성 조회하고 지정하기

Element 타입에는 비표준 HTML 속성과 XML 문서 요소의 속성을 조회하고 지정할 수 있는 getAttribute()와 setAttribute() 메서드가 있다.

var image=document.images[0];
var width=parseInt(image.getAttribute("WIDTH"));
image.setAttribute("class","thumbnail");

이 때 속성 이름은 대소문자를 구분하지 않는다.

Element 객체에는 속성과 관련된 메서드가 두개 더 있다.

hasAttribute(), removeAttribute() 속성 존재유무를 이름으로 확인하고 속성을 완전히 제거하는데 사용

다른 네임스페이스의 속성을 가진 XML 문서에서 작업한다면, 네임스페이스를 바꾸는 다음 네 개의 메서드를 사용할 수 있다. getAttributeNS(), setAttributeNS(), hasAttributeNS(), removeAttributeNS()

이 메서드들은 속성명 문자열 외에도 전달인자를 하나 더 취한다. 첫번째는 네임스페이스를 구분하는 URI이고, 두번째는 네임스페이스 안에서 지역 속성의 전체 이름이다. 그러나 setAttributeNS() 메서드만은 두번째 전달인자로 네임스페이스 접두사가 붙은 제한적인 속성 이름을 쓴다.

Dataset 속성

일반적으로 자바스크립트 코드로 HTML 요소를 선택하고 조작할 때, 추가 정보를 요소에 첨부할 수 있으면 유용하다. 이를 위해 class 속성에 특수 식별자를 추가하는 방법이 종종 쓰이지만, 어떤 경우에는 추가하려는 정보가 너무 복잡해서 클라이언트 측 프로그래머가 비표준 속성을 사용하게 된다. 이때 getAttriute()와 setAttributre()를 사용하면 HTML 문법의 적합성을 포기해야 한다 이때 해결책이 dataset이다. dataset 속성은 가시적인 표현에 영향을 주지 않는다

<span class="sparkline" data-ymin="0" data-ymax="10">
1 1 1 2 2 3 4 5 5 4 3 5 6 7 7 4 2 1
</span>

var sparklines=document.getElementsByClassName("sparkline");
for(var i=0;i<sparklines.length;i++){
  var dataset=sparklines[i].dataset;
  var ymin=parseFloat(dataset.ymin);
  var ymax=parseFloat(dataset.ymax);
  var data=sparklines[i].textContent.split(" ").map(parseFloat);
}

Attr 노드로서의 속성

Node 타입에는 attributes 프로퍼티가 존재한다. 이 프로퍼티의 값은 노드가 Element 객체가 아니면 null이고 Element 객체면 요소의 모든 속성(attributes)을 가리키는 읽기 전용의 유사 배열 객체가 된다. 

document.body.attributes[0]

document.body.attributes.bgcolor

document.body.attributes["ONLOAD"]

요소의 내용

HTML로서의 요소 내용

Element 객체의 innerHTML 프로퍼티는 해당 요소의 내용을 HTML 마크업 문자열로 반환한다. innerHTML을 통해서 값을 지정하면 항상 파싱이 일어나지만, 웹브라우저의 HTML 파싱 성능이 상당히 괜찮으므로 보통은 사용해도 상관없다.

outterHTML이 반환하는 HTML이나 XML 마크업 문자열은 프로퍼티를 사용한 요소를 나타내는 시작과 끝 태그를 포함한다. 요소의 outerHTML을 지정하면 새로운 내용은 해당 요소 자체를 교체한다. outerHTML은 Element 노드에만 정의되어 있으며, Document 객체에는 존재하지 않는다.

일반 텍스트로서의 요소 내용

Node 객체의 textContent 프로퍼티

var para=document.getElementsByTagName("p")[0];  //문서의 첫 <p>
var text=para.textContent;                      //text는 "This is a simple document."이다.
para.terxtContent="Hello World!";                //<p>의 내용 수정

Text 노드로서의 요소 내용

요소 내용을 다루는 다른 방법은 요소 내용을 자식 노드 목록으로 대하는 것이다. 이 자식 노드는 다시 자손 객체를 가질 수 있다.

// Return the plain-text content of element e, recursing into child elements.
// This method works like the textContent property
function textContent(e) {
    var child, type, s = "";  // s holds the text of all children
    for(child = e.firstChild; child != null; child = child.nextSibling) {
        type = child.nodeType;
        if (type === 3 || type === 4)  // Text and CDATASection nodes
            s += child.nodeValue;
        else if (type === 1)           // Recurse for Element nodes
            s += textContent(child);
    }
    return s;
}

노드의 생성, 삽입, 삭제

노드 생성

Document 객체의 createElement() 메서드로 새로운 Element 노드를 생성할 수 있다.

Text 노드도 이와 유사하다.

var s=document.createElement("script");

var newnode=document.createTextNode("text node content");

Document 객체에는 createComment() 같은 잘 쓰지 않는 객체 생성 팩터리 메서드도 있다.

모든 노드에는 스스로를 복제해서 반환하는 cloneNode() 메서드가 존재한다. 전달인자가 true이면 모든 자손 객체 복사, false이면 해당 객체만 복사. 첫번째 인자 : 다른 문서의 노드, 두번째 인자 : boolean값

노드 삽입

Node 객체의 메서드인 appendChild()나 insertBefore()로 생성한 노드를 문서에 삽입할 수 있다.

insertBefore()는 전달인자가 두개다. 첫번째 전달인자는 삽입하려는 코드, 두번째 전달인자는 노드가 삽입될 위치 앞에 있는 노드. 두번째가 null이면 appenChild()처럼 작동(자식 노드의 끝에 삽입)

// Sort the rows in first <tbody> of the specified table according to
// the value of nth cell within each row. Use the comparator function
// if one is specified. Otherwise, compare the values alphabetically.
function sortrows(table, n, comparator) {
    var tbody = table.tBodies[0]; // First <tbody>; may be implicitly created
    var rows = tbody.getElementsByTagName("tr"); // All rows in the tbody
    rows = Array.prototype.slice.call(rows,0);   // Snapshot in a true array

    // Now sort the rows based on the text in the nth <td> element
    rows.sort(function(row1,row2) {
        var cell1 = row1.getElementsByTagName("td")[n];  // Get nth cell
        var cell2 = row2.getElementsByTagName("td")[n];  // of both rows
        var val1 = cell1.textContent || cell1.innerText; // Get text content
        var val2 = cell2.textContent || cell2.innerText; // of the two cells
        if (comparator) return comparator(val1, val2);   // Compare them!
        if (val1 < val2) return -1;
        else if (val1 > val2) return 1;
        else return 0;
    });

    // Now append the rows into the tbody in their sorted order.
    // This automatically moves them from their current location, so there
    // is no need to remove them first. If the <tbody> contains any
    // nodes other than <tr> elements, those nodes will float to the top.
    for(var i = 0; i < rows.length; i++) tbody.appendChild(rows[i]);
}

// Find the <th> elements of the table (assuming there is only one row of them)
// and make them clickable so that clicking on a column header sorts
// by that column.
function makeSortable(table) {
    var headers = table.getElementsByTagName("th");
    for(var i = 0; i < headers.length; i++) {
        (function(n) {  // Nested funtion to create a local scope
            headers[i].onclick = function() { sortrows(table, n); };
        }(i));          // Assign value of i to the local variable n
    }
}

노드의 삭제와 교체

removeChild() 메서드는 문서 계층 구조에서 하나의 노드를 삭제한다. 주의할 점은 이 메서드는 삭제하려는 노드가 아니라 이 노드를 자식으로 가진 부모 노드에서 실행된다는 점이다. 메서드 전달인자로 삭제할 자식 노드를 넘기고, 부모 노드의 메서드로 호출한다. 문서의 n 노드를 삭제하기 위해선 n.parentNode.removeChild(n);

replaceChild()는 자식 노드 하나를 삭제하고 새 노드로 교체한다. 부모 노드에서 이 메서드를 실행할 때, 첫번째 전달인자로 새 노드를, 두번째 전달인자로는 교체될 노드를 넘긴다. 텍스트 문자열로 노드 n을 교체하기 위해선

n.parentNode.replaceChild(document.createTextNode("[REDACTED]"),n);

DocumentFragment 사용하기

DocumentFragment는 다른 노드를 담는 임시 컨테이너 역할을 하는 특수 목적의 노드다.

DocumentFragment의 특이한 점은 노드 집합을 단일 노드처럼 다룰 수 있다는 점이다. DocumentFragment를 appendChild(), insertBefore(), replaceChild()에 전달인자로 넘기면, 해당 객체가 아닌 객체의 자식 객체가 문서에 삽입된다.

//노드 n의 자손 객체를 역순 정렬한다.
function reverse(n){
  //임시 컨테이너로 사용할 빈 DocumentFragment를 생성한다.
  var f=document.createDocumentFragment();
  //n의 자식 객체를 역순으로 루프를 돌려서 f로 옮긴다.
  //n의 마지막 자식 객체는 f의 첫번째 자식 객체가 도디고 반대로 첫 객체는 마지막 객체가 된다.
  //주의할 것은 f에 추가하면 n에서는 지워진다는 점이다.
  while(n.lastChild) f.appendChild(n.lastChild);
  //끝으로, 다시 f의 모든 자식 객체를 n으로 한번에 보낸다.
  n.appenChild(f);
}

예제 : 내용 목차 생성하기

/**
 * TOC.js: create a table of contents for a document.
 * 
 * This module registers an anonymous function that runs automatically
 * when the document finishes loading. When it runs, the function first
 * looks for a document element with an id of "TOC". If there is no
 * such element it creates one at the start of the document.
 * 
 * Next, the function finds all <h1> through <h6> tags, treats them as
 * section titles, and creates a table of contents within the TOC
 * element. The function adds section numbers to each section heading
 * and wraps the headings in named anchors so that the TOC can link to
 * them. The generated anchors have names that begin with "TOC", so
 * you should avoid this prefix in your own HTML.
 * 
 * The entries in the generated TOC can be styled with CSS. All entries have
 * a class "TOCEntry". Entries also have a class that corresponds to the level
 * of the section heading. <h1> tags generate entries of class "TOCLevel1", 
 * <h2> tags generate entries of class "TOCLevel2", and so on. Section numbers
 * inserted into headings have class "TOCSectNum".
 *
 * You might use this module with a stylesheet like this:
 *
 *   #TOC { border: solid black 1px; margin: 10px; padding: 10px; }
 *   .TOCEntry { font-family: sans-serif; }
 *   .TOCEntry a { text-decoration: none; }
 *   .TOCLevel1 { font-size: 16pt; font-weight: bold; }
 *   .TOCLevel2 { font-size: 12pt; margin-left: .5in; }
 *   .TOCSectNum:after { content: ": "; }
 * 
 * That final line generates a colon and space after section numbers. To hide
 * the section numbers, use this:
 *   
 *   .TOCSectNum { display: none }
 *
 * This module requires the onLoad() utility function.
 **/
onLoad(function() { // Anonymous function defines a local scope
    // Find the TOC container element.
    // If there isn't one, create one at the start of the document.
    var toc = document.getElementById("TOC");
    if (!toc) {
        toc = document.createElement("div");
        toc.id = "TOC";
        document.body.insertBefore(toc, document.body.firstChild);
    }

    // Find all section heading elements
    var headings;
    if (document.querySelectorAll) // Can we do it the easy way?
        headings = document.querySelectorAll("h1,h2,h3,h4,h5,h6");
    else   // Otherwise, find the headings the hard way
        headings = findHeadings(document.body, []);

    // Recursively traverse the document body looking for headings
    function findHeadings(root, sects) {
        for(var c = root.firstChild; c != null; c = c.nextSibling) {
            if (c.nodeType !== 1) continue;
            if (c.tagName.length == 2 && c.tagName.charAt(0) == "H")
                sects.push(c);
            else 
                findHeadings(c, sects);
        }
        return sects;
    }

    // Initialize an array that keeps track of section numbers.
    var sectionNumbers = [0,0,0,0,0,0];

    // Now loop through the section header elements we found.
    for(var h = 0; h < headings.length; h++) {
        var heading = headings[h];

        // Skip the section heading if it is inside the TOC container.
        if (heading.parentNode == toc) continue;

        // Figure out what level heading it is.
        var level = parseInt(heading.tagName.charAt(1));
        if (isNaN(level) || level < 1 || level > 6) continue;

        // Increment the section number for this heading level
        // and reset all lower heading level numbers to zero.
        sectionNumbers[level-1]++;
        for(var i = level; i < 6; i++) sectionNumbers[i] = 0;

        // Now combine section numbers for all heading levels
        // to produce a section number like 2.3.1.
        var sectionNumber = sectionNumbers.slice(0,level).join(".")

        // Add the section number to the section header title.
        // We place the number in a <span> to make it styleable.
        var span = document.createElement("span");
        span.className = "TOCSectNum";            
        span.innerHTML = sectionNumber;                
        heading.insertBefore(span, heading.firstChild);

        // Wrap the heading in a named anchor so we can link to it.
        var anchor = document.createElement("a");
        anchor.name = "TOC"+sectionNumber; 
        heading.parentNode.insertBefore(anchor, heading);
        anchor.appendChild(heading);

        // Now create a link to this section.
        var link = document.createElement("a");
        link.href = "#TOC" + sectionNumber; // Link destination
        link.innerHTML = heading.innerHTML; // Link text is same as heading

        // Place the link in a div that is styleable based on the level.
        var entry = document.createElement("div");
        entry.className = "TOCEntry TOCLevel" + level; 
        entry.appendChild(link);

        // And add the div to the TOC container.
        toc.appendChild(entry);
    }
});

문서와 요소의 기하학적 특징과 스크롤

문서 좌표와 뷰포트 좌표

브라우저의 요소는 위치를 픽셀(pixel) 단위로 측정한다. 우측으로 증가하면 x축이고 아래쪽으로 증가하면 y축이다. 요소의 x,y 좌표는 문서의 좌측 상단 모서리나 문서를 출력한 '뷰표트'의 좌측 상단 모서리를 기준점으로 삼는다.(뷰표트란 최상위 창과 탭에서 실제로 문서 내용이 출력되는 브라우저 영역이다. 메뉴와 툴바, 탭 메뉴같이 별로 중요하지 않은 것을 제외한 영역이다.) 프레임 안에 출력된 문서에서는 <iframe>요소가 뷰표트다. 

문서가 뷰표트보다 작거나 스크롤이 없다면, 문서의 상단 좌측 모서리는 뷰포트의 상단 좌측 모서리가 되고 이때 문서와 뷰표트의 좌표계는 같다. 하지만 일반적으로 두 좌표계를 전환하기 위해서는 스크롤 오프셋을 빼거나 더해야 한다. 예를 들어 어떤 요소가 y축으로 문서 좌표 200픽셀에 있고 사용자가 브라우저를 75픽셀 스크롤해서 내렸다면, 이 요소의 뷰포트 좌표는 Y 좌표 125픽셀이다.

창의 스크롤바 위치 가져오기

// Return the current scrollbar offsets as the x and y properties of an object
function getScrollOffsets(w) {
    // Use the specified window or the current window if no argument
    w = w || window;

    // This works for all browsers except IE versions 8 and before
    if (w.pageXOffset != null) return {x: w.pageXOffset, y:w.pageYOffset};

    // For IE (or any browser) in Standards mode
    var d = w.document;
    if (document.compatMode == "CSS1Compat")
        return {x:d.documentElement.scrollLeft, y:d.documentElement.scrollTop};

    // For browsers in Quirks mode
    return { x: d.body.scrollLeft, y: d.body.scrollTop };
}

창의 뷰포트 크기 가져오기

// Return the viewport size as w and h properties of an object
function getViewportSize(w) {
    // Use the specified window or the current window if no argument
    w = w || window;  

    // This works for all browsers except IE8 and before
    if (w.innerWidth != null) return {w: w.innerWidth, h:w.innerHeight};

    // For IE (or any browser) in Standards mode
    var d = w.document;
    if (document.compatMode == "CSS1Compat")        return { w: d.documentElement.clientWidth,
                 h: d.documentElement.clientHeight };

    // For browsers in Quirks mode
    return { w: d.body.clientWidth, h: d.body.clientWidth };
}

요소의 기하학적 특징 조회

어떤 요소의 크기와 위치를 알아내는 가장 쉬운 방법은 getBoundingClientRect() 메서드를 호출하는 것이다.

이 메서드가 반환하는 요소 위치의 기준은 뷰포트 좌표다. 사용자 스크롤 여부와 관계없는 문서 좌표로 변경하려면 스크롤 오프셋을 더해야 한다.

var box=e.getBoundingClientRect(); //뷰포트 좌표로 위치 가져오기
var offsets=getScrollOffsets();    //앞에서 정의한 유틸 함수 사용
var x=box.left+offsets.x;          //문서 좌푤 변환
var y=box.top+offsets.y;

지정한 위치의 요소 알아내기

뷰포트에서 지정한 위치에 어떤 요소가 있는지 알아내야 할 경우 Document 객체의 elementFromPoint() 메서드를 사용

이 메서드에 전달인자로 뷰포트 기준 x,y 좌표를 넘기면, 지정한 위치에 있는 Element 객체가 반환한다.

스크롤

Window 객체의 scrollTo() 메서드는 문서 기준 x,y 좌표를 넘겨서 스크롤바 오프셋을 지정한다. 이러면 창이 스크롤되고, 지정한 x,y 좌표가 뷰포트의 좌측 상단 코너에 위치한다.

다음 코드는 브라우절르 스크롤해서 문서의 가장 아래쪽을 보여준다.

//문서와 뷰포트의 높이를 가져온다. offsetHeight는 아래에 설명한다.
var documentHeight=document.documentElement.offsetHeight;
var viewportHeight=window.innerHeight;
//그리고 스크롤되어서 뷰포트의 마지막 페이지를 보여준다.
window.scrollTo(0,documentHeight-viewportHeight);

Window 객체의 scrollby() 메서드는 전달인자를 현재 스크롤바 오프셋에 더한다.

scrollIntoView() 메서드는 호출한 요소가 뷰포트 안에 노출되도록 보장한다. 전달인자로 false를 넘기면 뷰포트 하단에 요소의 하단 가장자리를 가능한 붙이려 한다. 이 동작은 이름이 잇는 anchor 링크에 window.location.hash를 지정했을 때 브라우저가 하는 동작과 비슷하다.

요소의 크기와 위치, 오버플러

모든 HTML 요소에는 x,y 좌표를 반환하는 offsetLeft와 offsetTop 프로퍼티가 존재한다. 대다수 요소에서 이 좌표 값은 문서 기준이며 요소의 위치를 직접 나타낸다. 하지만 이 프로퍼티는 몇몇 요소에서 부모 요소를 기준으로 좌표를 반환한다. offsetParent 프로퍼티가 이 반환 좌표 값과 관련된 내용을 명시한다. offsetParent가 null일 때만 offsetLeft와 offsetTop 프로퍼티가 문서 기준 좌표이다. 그러므로 다음 반복문을 순회해야 한다.

function getelementPosition(e){
  var x=0,y=0;
  while(e!=null){
    x+=e.offsetLeft;
    y+=e.offsetTop;
    e=e.offsetParent;
  }
  return {x:x, y:y}
}

프로퍼티명이 offset으로 시작하는 프로퍼티의 집합 말고도, 모든 문서 요소에는 두 가지 프로퍼티 그룹이 더 존재한다.

client와 scroll로 시작하는 프로퍼티 집합

offsetWidth   clientWidth   scrollWidth

offsetHeight  clientHeight  scrollHeight

offsetLeft      clientLeft      scrollLeft

offsetTop      cleintTop      scrollTop

offsetParent

*컨테이너 요소보다 요소의 내용이 더 클 수도 있다. 따라서 각 요소에는 스크롤바 있을 수 있다.

client가 offset과 다른점은 내용, 패딩만 포함하고 테두리 크기는 포함하지 않는다. 또한 브라우저가 패딩과 테두리 사이에 스크롤바를 넣으면 client는 스크롤바 크기를 포함시키지 않는다. 그리고 <i>, <code>, <span> 같은 인라인 요소에서는 0을 반환. 예외적으로 문서의 루트 요소에서 이 프로퍼티를 사용할 때는 innerWidth와 innerHeight 프로퍼티와 같은 값 반환. clientLeft, clientTop 프로퍼티는 그리 유용하지 않다. 요소의 패딩 바깥쪽과 테두리 바깥쪽 사이의 가로, 세로 거리를 반환하는데 보통 상단의 테두리 너비와 같다. 인라인 요소의 경우는 항상 0이다.

scrollWidth와 scrollHeight는 요소의 내용 영역과 패딩, 넘쳐서 안보이는 내용을 합친 크기다. 요소의 내용이 내용 영역 크기와 맞아 떨어지면, 이 프로퍼티는 clientWidth와 clientHeight 프로퍼티 값과 같다. scrollLeft, scrollTop은 요소의 스크롤바 위치를 반환한다. scrollLeft, scrollTop은 쓰기가 가능한 프로퍼티로서 값을 지정하여 요소 안의 내용을 스크롤 할 수 있다.

내용 영역보다 큰 내용을 가진 스크롤 가능한 요소를 문서가 담고 있을 때, 앞에서 정의한 getElementPosition() 메서드는 스크롤바 위치가 고려되지 않기 때문에 정상적으로 작동하지 않는다. 수정버전

function getElementPos(elt){
  var x=0,y=0;
  for(var e=elt;e!=null;e=e.offset(Parent){
    x+=e.offsetLeft;
    y+=e.offsetTop;
  }
  //스크롤 오프셋을 빼기 위해 모든 조상 요소에 대해 다시 루프문을 순회한다.
  //브라우저 창의 스크롤바는 제외하고 뷰포트 좌표로 변환한다.
  for(var e=elt.parentNode; e!=null && e.nodeType==1; e=e.parentNode){
    x-=e.scrollLeft;
    y-=e.scrollTop;
  }
  return {x:x, y:y};
}

getBoundingClientRect()와 같음. 근데 문제가 많아(jQuery 라이브러리 이용 추천)

HTML 폼

폼과 관련 요소의 선택하기

var fields=document.getElementById("address").getElementsByTagName("input");

//id="shipping"인 폼 안의 모든 라디오 버튼
document.querySelectorAll('#shipping input[type="radio"]');
//id="shipping"인 폼 안에서 name="method"인 모든 라디오 버튼
document.querySelectorAll('#shipping input[type="radio"][name="method"]');

name="address"로 설정된 <form> 요소를 선택하는 방법들

window.address //하지마
document.address 
document.forms.address //위보다 정확
document.forms[n] //의도되로 안돼

name 속성이 'address'인 품 안의 첫번째 요소의 name 속성이 'street'

document.forms.address[0]
document.forms.address.street
document.address.street  /id="address"가 아니고 name="address"만

좀더 명확히

document.forms.address.elements[0]
document.forms.address.elements.street

id 속성은 특정 문서 요소에 이름을 붙일 때 일반적으로 선호하는 방법이다. 반면에 name 속성은 HTML 폼 전송이라는 특별한 목적을 가지고 있다.

폼과 관련 요소의 프로퍼티

Form 객체 중 가장 흥미로운 프로퍼티는 elements[] 배열이다.

자바스크립트의 Form 객체에는 같은 목적으로 사용하는 submit()과 reset() 메서드가 있다. Form 객체의 submit() 메서드를 실행하면 폼을 전송하고, reset()을 실행하면 폼 요소를 초기화한다.

type

폼 요소의 타입을 구분하기 위한 읽기 전용 문자열이다. 

form

현재 요소를 담고 있는 Form 객체에 대한 읽기 전용 참조 프로퍼티. 포함되어있지 않으면 null반환

name

HTML name 속성에 명시되어 있는 읽기 전용 문자열

value

폼 관련 요소에 포함되거나 출력된 값을 명시한, 읽고 쓰기가 가능한 문자열 폼이 전송될 때 웹 서버로 보내진다.

폼과 관련 요소의 이벤트 핸들러

각 <form> 요소에는 폼 전송을 감지하는 onsubmit 이벤트 핸들러와 폼 초기화를 감지하는 onreset 이벤트 핸들러가 존재한다. onsubmmit 핸들러는 폼이 전송 되기 직전에 실행된다. 여기서 false를 반환하여 전송을 취소할 수 있다.

onSubmit은 submit 버튼을 클릭했을 때만 발생

onRest도 reset 버튼을 클릭했을 때만 발생

click이나 change이벤트 발생 onclick, onchange핸들러 사용

폼 요소가 키보드 입력 커서를 받는 순간 focus 이벤트 발생, 떠날때 blur 이벤트 발생

핸들러 안에서 this는 이벤트를 발생시킨 문서 요소를 참조

푸시 버튼

버튼 요소에는 기본 동작이 없으며, onclick 이벤트 핸들러 없이는 사용되지 않는다.

input 요소로 정의한 버튼은 value 속성의 텍스트를 출력

button 요소로 정의한 버튼은 요소의 내용을 출력

전송 버튼과 초기화 버튼 요소는 일반 버튼 요소와 비슷하지만 폼을 전송하고 초기화하는 기본동작을 갖고 있다.

토글 버튼

체크박스와 라디오 요소는 토글 버튼이거나 체크 유무를 알 수 있는 두가지 상태가 있는 버튼이다. 사용자는 요소를 클릭해서 토글 버튼의 상태를 바꿀 수 있다. 그룹 안 라디오 요소들의 name 속성 값을 똑같이하면 서로 배타적이 된다. 즉, 하나를 체크하면 먼저 체크했던 요소는 체크가 풀린다. 체크박스도 마찬가지

라디오와 체크박스 요소에는 모두 checked 프로퍼티가 존재한다. 이는 읽고 쓰기가 가느으한 불리언 값으로, 요소가 현재 체크되었는지 여부를 나타낸다. defaultChecked 프로퍼티 존재.

사용자가 토글 버튼을 클릭했을 때, 라디오나 체크박스 요소는 onclick 핸들러를 실행시킨다. 클릭해서 토글 버튼의 상태가 바뀌면, onchange 이벤트 핸들러도 실행

텍스트 필드

placeholder는 사용자가 입력하기 전에 필드에 출력할 안내 문구

각 텍스트 입력 요소들에는 onkeypress, onkeydown, onkeyup 이벤트 핸들러 존재

Select와 Option 문서 요소

select 요소는 사용자가 선택할 수 있는 선택 목록(option 요소로 표현)을 나타낸다. 일반적으로 드롭다운 메뉴 형태. size 속성에 1보다 큰 값을 지정하면 스크롤 가능. multiple 속성이 있는 select 요소는 사용자가 옵션을 여러개 선택할 수 있다.(type='select-multiple') default는 'select-one'. select 요소에 출력되는 옵션들은 토글 버튼이 아니고 option 요소로 정의된다. select 요소에는 options 프로퍼티가 정의되어 있는데, 이 프로퍼티는 Options 요소들을 내장한 유사 배열 객체.

<select id="num" onchange="selectNum()">

<option value = "1" selected>하나</option>

<option value = "2">둘</option>

</select>

Document 객체의 다른 특징들

Document 프로퍼티

Document 객체에는 흥미로운 프로퍼티가 몇 가지 더 존재한다.

cookie

자바스크립트 프로그램이 HTTP 쿠키를 읽고 쓸 수 있게 하는 프로퍼티다.

domain

웹 페이지 간의 상호작용에서 주 도메인이 같은 믿을 수 있는 웹 서버들의 동일 출처 정책과 관련된 보안 제약 사항을 호의적으로 완화시킨다.

lastModified

문서 수정 날짜를 담고 있는 문자열

location

이 프로퍼티는 Window 객체의 location 프로퍼티처럼 location 객체를 참조한다.

referrer

현재 문서로 브라우저를 이끈 링크를 담은 문서 URL 사용자가 주소창에 직접 입력한 경우는 해당하지 않는다. 이 프로퍼티는 HTTP Referer 헤더와 내용이 같지만 철자에 r이 두개다.

싱기싱기!!

title

문서의 <title>과 </title> 태그 사이에 있는 텍스트

URL

Document 객체의 읽기 전용 URL 문자열로 Location 객체와는 다르다. 이 프로퍼티의 값은 location.href의 초기 값과 같지만 location 객체처럼 동적이지는 않다. 예를 들어 사용자가 문서 내에서 부분 식별자를 통해 이동했다면, location.href는 바뀌지만 document.URL은 바뀌지 않는다.

document.write() 메서드

write()는 HTML을 출력하기 위해서 현재 문서가 파싱되는 동안에만 사용할 수 있다. 즉 <script> 요소의 전역 레벨의 코드에서만 document.write()를 호출할 수 있다.

선택한 텍스트 조회하기

window.getSelection()은 순차적인 하나 이상의 Range 객체로 현재 선택된 내용을 가리키는 Selection 객체를 반환한다.

편집 가능한 내용

요소의 내용을 편집 가능하게 만들기 위해서는 어떤 태그에 contenteditable이라는 HTML 속성을 지정하거나, Element 객체에 자바슼크립트로 contenteditable 프로퍼티를 지정하면 된다.

Document 객체의 designMode 프로퍼티를 문자열 "on"으로 지정하면 문서 전체를 편집 모드로 만들 수도 있다. designMode 프로퍼티는 이에 부합하는 HTML 속성이 없기 때문에 <iframe>의 문서를 편집 모드로 만들 수도 있다.

<iframe id="editor" src="about:blank"></iframe> //빈 iframe
<script>
onLoad(function(){
  var editor=document.getElementById("editor");
  editor.contentDocument.designMode="on";
});
</script>

 

728x90
LIST

' > 자바스크립트 완벽 가이드' 카테고리의 다른 글

17장 이벤트 다루기  (0) 2020.11.23
16장 CSS 다루기  (0) 2020.11.22
14장 window 객체  (0) 2020.11.20
13장 웹브라우저의 자바스크립트  (0) 2020.11.19
12장 서버 측 자바스크립트  (0) 2020.11.15
댓글
공지사항