innerHTML에 비해 createElement의 장점은 무엇입니까?


80

실제로 innerHTML보다 createElement를 사용하면 어떤 이점이 있습니까? innerHTML을 사용하는 것이 성능 및 코드 가독성 / 유지 보수 측면에서 더 효율적이라고 확신하기 때문에 질문하고 있지만 팀원들은 코딩 접근 방식으로 createElement를 사용하기로 결정했습니다. createElement가 어떻게 더 효율적일 수 있는지 이해하고 싶습니다.


답변:


77

Pekka가 이미 언급했듯이 안전 외에도 createElement수정 innerHTML(이미있는 것을 버리고 교체하는 것과는 반대로) 대신 사용 하는 데는 몇 가지 이점이 있습니다 .

요소를 추가 할 때 DOM 요소에 대한 기존 참조를 유지합니다.

에 추가 (또는 수정) 할 때 innerHTML해당 요소 내의 모든 DOM 노드를 다시 구문 분석하고 다시 만들어야합니다. 노드에 대한 참조를 저장하면 더 이상 표시되지 않기 때문에 본질적으로 쓸모가 없습니다.

DOM 요소에 연결된 이벤트 핸들러를 보존합니다.

이것은 마지막 경우의 특별한 경우 (일반적이지만)입니다. 설정 innerHTML은 생성 된 새 요소에 이벤트 핸들러를 자동으로 다시 연결하지 않으므로 직접 추적하고 수동으로 추가해야합니다. 이벤트 위임은 경우에 따라이 문제를 해결할 수 있습니다.

어떤 경우에는 더 간단하고 빠를 수 있습니다.

많은 추가 작업을 수행하는 경우, innerHTML단순한 변경의 경우 더 빠르지 만 반복적으로 다시 구문 분석하고 요소를 만드는 속도가 더 느리기 때문에 재설정을 계속하고 싶지는 않습니다 . 이 문제를 해결하는 방법은 HTML을 문자열로 작성하고 innerHTML완료되면 한 번 설정 하는 것입니다. 상황에 따라 문자열 조작은 단순히 요소를 만들고 추가하는 것보다 느릴 수 있습니다.

또한 문자열 조작 코드가 더 복잡 할 수 있습니다 (특히 안전을 원하는 경우).

다음은 사용하기 더 편리하도록 가끔 사용하는 기능 createElement입니다.

function isArray(a) {
    return Object.prototype.toString.call(a) === "[object Array]";
}

function make(desc) {
    if (!isArray(desc)) {
        return make.call(this, Array.prototype.slice.call(arguments));
    }

    var name = desc[0];
    var attributes = desc[1];

    var el = document.createElement(name);

    var start = 1;
    if (typeof attributes === "object" && attributes !== null && !isArray(attributes)) {
        for (var attr in attributes) {
            el[attr] = attributes[attr];
        }
        start = 2;
    }

    for (var i = start; i < desc.length; i++) {
        if (isArray(desc[i])) {
            el.appendChild(make(desc[i]));
        }
        else {
            el.appendChild(document.createTextNode(desc[i]));
        }
    }

    return el;
}

다음과 같이 부르면 :

make(["p", "Here is a ", ["a", { href:"http://www.google.com/" }, "link"], "."]);

다음과 같은 HTML을 얻을 수 있습니다.

<p>Here is a <a href="http://www.google.com/">link</a>.</p>

5
DOM 트리 조각을 분리하여 생성 한 다음 마지막에 한 번 실제 DOM에 연결하는 것의 속도 이점도 있습니다.
staticsan

@staticsan, 그것은 좋은 지적입니다 (그리고 make함수가 조금 더 쉽게 만들 수있는 또 다른 것 ).
매튜 크럼 리

DOM 조각을 분리하여 생성하면 확실히 삽입 속도가 빨라집니다. innerHTML과 관련하여 동일한 접근 방식도 사용할 수 / 있어야합니다. 일반적으로 HTML 문자열을 배열로 푸시 한 다음 기본 제공 배열 조인 함수 (내 경험상 가장 빠르다)를 사용하여 요소를 조인 한 다음이를 DOM에 추가합니다.
oninea

@Matthew Crumley : IYO, 위에서 언급 한 장점 (모두 유효 함)이 실제 사용에서 innerHTML을 사용하는 장점보다 더 크다고 생각하십니까? 보다 구체적으로, 테이블을 동적으로 빌드하는 코드를 작성한다고 가정 해 봅시다 (매우 일반적인 코딩 작업). 두 접근 방식 모두에 대해 createElement 또는 innerHTML 빌드 조각을 사용 하시겠습니까?
oninea

1
나는 일반적으로 innerHTML단점 때문에 피 합니다. 복잡한 마크 업 (예 : 테이블 작성)의 경우 일반적으로 마크 업의 각 부분을 생성하는 함수를 작성합니다. 예를 들어, tr각 행의 데이터에서 를 생성하는 함수가 있습니다 . 그런 다음 행을 테이블로 결합하는 다른 기능이있을 수 있습니다. 각 함수 make는 적절한 인수 로 호출 하는 것처럼 간단 할 수 있습니다 . 성능이 문제가되면 HTML 문자열을 반환하도록 함수를 변경할 수 있습니다.
매튜 크럼 리

14

innerHTML더 빠를 수 있지만 가독성이나 유지 관리 측면에서 더 좋다는 데 동의하지 않습니다. 모든 것을 하나의 문자열에 넣는 것이 더 짧을 수 있지만 코드가 짧다고 항상 유지 관리가 더 쉬운 것은 아닙니다.

문자열 연결은 더하기 '와 따옴표 열기 및 닫기를 추적하기 어려워 지므로 동적 DOM 요소를 만들어야하는 경우에만 확장되지 않습니다. 다음 예를 고려하십시오.

결과 요소는 콘텐츠가 동적 인 두 개의 내부 범위가있는 div입니다. 첫 번째 범위 내의 클래스 이름 (전사) 중 하나도 동적입니다.

<div>
    <span class="person warrior">John Doe</span>
    <span class="time">30th May, 2010</span>
</div>

다음 변수가 이미 정의되어 있다고 가정합니다.

var personClass = 'warrior';
var personName = 'John Doe';
var date = '30th May, 2010';

innerHTML 만 사용하고 모든 것을 단일 문자열로 매싱하면 다음을 얻을 수 있습니다.

someElement.innerHTML = "<div><span class='person " + personClass + "'>" + personName + "</span><span class='time'>" + date + "</span></div>";

위의 혼란은 매번 문자열을 열고 닫지 않도록 문자열 교체를 사용하여 정리할 수 있습니다. 간단한 텍스트 교체의 경우에도 replace문자열 연결 대신 사용 하는 것을 선호합니다 .

이것은 키와 대체 값의 객체를 가져와 문자열에서 대체하는 간단한 함수입니다. 키가 $특수 값임을 나타 내기 위해 접두사가 붙은 것으로 가정합니다 . $대체 값 등에 나타나는 이스케이프 또는 가장자리 케이스를 처리하지 않습니다 .

function replaceAll(string, map) {
    for(key in map) {
        string = string.replace("$" + key, map[key]);
    }
    return string;
}

var string = '<div><span class="person $type">$name</span><span class="time">$date</span></div>';
var html = replaceAll(string, {
    type: personClass,
    name: personName,
    date: date
});
someElement.innerHTML = html;

객체를 구성하는 동안 속성, 텍스트 등을 분리하여 요소 구성을보다 프로그래밍 방식으로 제어함으로써 개선 할 수 있습니다. 예를 들어 MooTools를 사용하면 객체 속성을 맵으로 전달할 수 있습니다. 이것은 확실히 더 유지 관리가 가능하며 더 읽기 쉽다고 주장합니다. jQuery 1.4는 유사한 구문을 사용하여 DOM 객체를 초기화하기위한 맵을 전달합니다.

var div = new Element('div');

var person = new Element('span', {
    'class': 'person ' + personClass,
    'text': personName
});

var when =  new Element('span', {
    'class': 'time',
    'text': date
});

div.adopt([person, when]);

아래의 순수한 DOM 접근 방식을 위의 것보다 더 읽기 쉽게 부르지는 않겠지 만 여는 / 닫는 따옴표와 수많은 더하기 기호를 추적 할 필요가 없기 때문에 확실히 유지 관리가 더 쉽습니다.

var div = document.createElement('div');

var person = document.createElement('span');
person.className = 'person ' + personClass;
person.appendChild(document.createTextNode(personName));

var when = document.createElement('span');
​when.className = 'date​​​​​​';
when.appendChild(document.createTextNode(date));

​div.appendChild(person);
div.appendChild(when);

가장 읽기 쉬운 버전은 일종의 자바 스크립트 템플릿 을 사용한 결과 일 가능성이 큽니다 .

<div id="personTemplate">
    <span class="person <%= type %>"><%= name %></span>
    <span class="time"><%= date %></span>
</div>

var div = $("#personTemplate").create({
    name: personName,
    type: personClass,
    date: date
});

@Anurag : innerHTML에 대한 첫 번째 예제에서는 이런 식으로 작성하는 경향이 있습니다. 조금 더 오래 알지만 개인적으로 읽을 수 있으며 조각의 구조도 유지됩니다. 그것에 대한 의견이 있습니까? array.push ( "<div>"); array.push ( "<span class = '", person + personClass, "'>", personName, "</ span>"); array.push ( "<span class = '", 시간, "'>", 날짜, </ span> "); array.push ("</ div> "); someElement.innerHTML = array.join (" " )
oninea

backticks 안에 코드를 넣으십시오 var a = hello.하지만 귀하의 코드를 알아낼 수 있습니다. 하나의 연결된 문자열보다 더 읽기 쉬워 보입니다.
Anurag

또한 태그 / 속성 열기 및 닫기를 처리하는 것은 여러 단계로 나눠져 있어도 여전히 성가신 상태입니다. 대신, 문자열 교체는 훨씬 깨끗하고 오류가 없습니다. 내 대답에 위의 예를 추가했습니다. IE에 필요한 문자열 구성 마이크로 최적화를 놓칠 수 있지만 실제로 필요한 경우가 아니면 하나의 브라우저에 대한 코드를 복잡하게 만들지 않아야합니다.
Anurag


나는 그 예가 어떤 것을 증명할만큼 충분히 결정적이라고 생각하지 않습니다. innerHTML 및 수동 구성을 사용하여 다양한 크기와 복잡성 (깊이)의 DOM을 만들어야하며 결과가 일관 적이면 innerHTML이 더 느리다는 것을 분명히 보여줍니다. 또한이 테스트는 렌더링 엔진이 body 태그에 대해 전체 DOM을 무효화하고 재구성하도록하기 때문에 매우 왜곡됩니다. 다른 예제에서는 DOM에 새 노드 만 추가하고 body 태그의 다른 부분을 건드리지 않습니다.
Anurag

14

사용자 bobince 는 jQuery에 대한 그의 비판 에서 여러 가지 단점을 매우 잘 설명 합니다.

... 또한 document.createElement ( 'div') 및 텍스트 노드를 사용하지 않고 $ ( "+ message + '')라고 말하여 div를 만들 수 있습니다. 만세! 단지 ... 잠시만 요. HTML에서 벗어나지 않았고 이번에는 클라이언트 측에서만 크로스 사이트 스크립팅 보안 허점을 만들었을 것입니다. 그리고 서버 측에서도 htmlspecialchars를 사용하기 위해 PHP를 너무 오랫동안 정리 한 후에. 부끄러운 일입니다. 아 글쎄, 아무도 정확성이나 보안에 대해 정말로 신경 쓰지 않습니까?

jQuery는 이것에 대해 전적으로 책임이 없습니다. 결국 innerHTML 속성은 몇 년 동안 사용되어 왔으며 이미 DOM보다 더 인기가 있음이 입증되었습니다. 그러나 라이브러리는 확실히 그러한 코딩 스타일을 장려합니다.

성능에 관해서 : InnerHTML은 파싱되고 내부적으로 DOM 요소로 변환되어야하기 때문에 (아마도createElement 메서드를 ) 입니다.

InnerHTML은 다음에 따라 모든 브라우저에서 더 빠릅니다. 제공 quirksmode 벤치 마크 .

가독성 과 사용 편의성에 관해서 는 대부분의 프로젝트에서 요일을 선택 innerHTML하는 것이 createElement좋습니다. 그러나 보시다시피 createElement.


1
@Pekka 정말 느려진 게 확실해 innerHTML? 나는 그것이 확실히 거짓이라는 것을 오랫동안 알고 있습니다. innerHTML실제로 사용 하는 것은 일부 브라우저 (추측)의 극적인 성능 이점 때문에 프레임 워크 내부에서 인기를 얻었습니다.
Pointy

@Pointy 흥미로운. 나는 벤치 마크가 없지만 상식적으로 innerHTML 더 느려 야 한다고 말한다 . 그것은 파싱되고, 검증되고, DOM 요소로 바뀌어야한다 createElement. 이것은 첫 단계에서하는 일이다. 그렇지 않다고 말하는 벤치 마크를 알고 있다면 기꺼이 바로 잡을 것입니다.
Pekka

3
quirksmode 벤치 마크는 조금 오래되었습니다. quirksmode.org/dom/innerhtml.html
Pointy

@Pointy still : 와우! 내가 생각했던 것에 완전히 반대합니다. 건배, 내 대답을 업데이트합니다.
Pekka

1
통찰력 Pekka와 Pointy에 감사드립니다. 이것은 innerHTML이 (코딩 경험 외에도) 더 빠르다는 내 견해를 강화합니다. 또한 배열에서 html 문자열을 사용하면 추가 성능 향상과 가독성도 제공됩니다. 내가 알고 싶은 것은 createElement가 그것을 사용하는 데 어떤 점이 있는지 여부입니다.
oninea

8

코드에서 참조를 유지하려면 createElement를 사용해야합니다. InnerHTML은 때때로 발견하기 어려운 버그를 생성 할 수 있습니다.

HTML 코드 :

<p id="parent">sample <span id='test'>text</span> about anything</p>

JS 코드 :

var test = document.getElementById("test");

test.style.color = "red"; //1 - it works

document.getElementById("parent").innerHTML += "whatever";

test.style.color = "green"; //2 - oooops

1) 색상을 변경할 수 있습니다.

2) 위의 줄에서 innerHTML에 무언가를 추가하고 모든 것이 다시 생성되고 더 이상 존재하지 않는 것에 액세스 할 수 있기 때문에 더 이상 색상이나 다른 것을 변경할 수 없습니다. 변경하려면 다시 getElementById 해야합니다 .

모든 이벤트에도 영향을 미친다는 점을 기억해야합니다. 이벤트를 다시 신청해야합니다.

InnerHTML은 더 빠르고 읽기가 더 쉬우므로 훌륭하지만주의해서 사용해야합니다. 당신이 무엇을하고 있는지 안다면 당신은 괜찮을 것입니다.


0

템플릿 리터럴 (템플릿 문자열)은 또 다른 옵션입니다.

const container = document.getElementById("container");

const item_value = "some Value";

const item = `<div>${item_value}</div>`

container.innerHTML = item;

이 방법을 사용할 때 HTML 주입에주의하십시오.
sean
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.