본문 바로가기

html / javascript / css

동적 스타일 - 자바스크립트로 CSS 다루기


소개

당신은 여기까지 자바스크립트를 배워 오면서 기본적인 구조와 DOM을 이용해서 요소를 선택하는 방법을 배웠으며, 선택한 요소를 다루는 방법도 익혔다.

이 글에서는, 자바스크립트를 사용해서 CSS를 조작하고 요소에 사용된 스타일을 동적으로 변경하는 방법을 다루고자 한다. 사실 이 방법들은 이미 다루어진 것 들이지만 CSS DOM과 작업할 때 조금 더 염두에 두어야 할 것들이 있다. 이것들에 대해서 이야기해보자.

스타일시트에 접근하기

브라우저는 기본적으로 스타일시트와 연동하기 위한 인터페이스를 제공한다. 자바스크립트 코드에서는, document.styleSheets 를 이용해서 스타일시트 목록에 접근할 수 있다. document.styleSheets 는 현재 페이지에 적용된 모든 스타일시트의 목록을 돌려주는데, link 로 가져오는 외부 스타일시트와 함께 style 요소 내부에 있는 내부 스타일시트도 가져오게 된다. style 요소가 id 속성을 갖고 있다면, document.getElementById(element_id) 을 사용해서 빠르게 참조할 수 있다.

또한 문서에 새로운 스타일시트를 추가할 수 있는데, document.createElement 메서드를 사용해서 새로운 style 요소를 만들어낼 수 있다. 사용자들이 약간의 조작을 통해 사이트의 스타일시트를 동적으로 변경할 수 있는 옵션을 제공하고자 한다면, 이러한 기능을 이용해서 할 수 있다. 새로운 스타일시트를 만들어내는 예제를 살펴보자:

var sheet = document.createElement('style')
sheet.innerHTML = "div {border: 2px solid black; background-color: blue;}";
document.body.appendChild(sheet);

스타일시트를 제거하는 것 역시 아주 간단하다. 첫째, 제거하고자 하는 스타일시트를 확실히 정해야 하는데, 아래에 있는 예제에서처럼 document.getElementById 메서드를 사용하면 된다. 스타일시트를 제거하기 위해, DOM 함수인 parent.removeChild(element) 를 이용할 수 있으며, 여기에서 element 은 당신이 제거하고자 하는 스타일시트 요소이며 parent 는 제거하고자 하는 스타일시트의 부모 요소이다. 아래의 예제에서처럼, 스타일시트(sheetToBeRemoved)를 제거하기 위해서는 먼저 스타일시트의 부모를 확인하고 - var sheetParent = sheetToBeRemoved.parentNode - 다음으로 sheetToBeRemoved 인수를 가진 removeChild 메서드를 호출하면 된다 - sheetParent.removeChild(sheetToBeRemoved) - 이렇게 말이다.

var sheetToBeRemoved = document.getElementById('styleSheetId');
var sheetParent = sheetToBeRemoved.parentNode;
sheetParent.removeChild(sheetToBeRemoved);

스타일시트 접근 예제 에서는 모든 스타일시트에 접근하고, 새로운 스타일시트를 추가하거나 그 중 하나를 제거하는 예를 보여주고 있다.

스타일시트 속성

stylesheet 요소는 자바스크립트를 통해 접근할 수 있으며, 현재 문서에서 참조하는 스타일시트에 관한 정보들 - 예를 들어 비활성화되었는지, 어디에 위치했는지, 포함하는 CSS 규칙들의 목록 등 - 을 얻을 수 있다. stylesheet 요소의 전체 속성(그리고 그밖에 여러가지 것들)을 보려면, W3C DOM 스타일시트 명세서를 체크해보기 바란다.

(아직은)이론적인 예제를 하나 고려해보자 - 기술 문서들을 보여주는 사이트를 갖고 있다고 가정하자. 문서들 중 일부를, 멋진 애니메이션을 가진 회전목마carousel에 넣어서 강조하고 싶다. 하지만, 어떤 이유에서든, 자바스크립트가 꺼져 있는 사용자에 대해서는 어떻게 할 것인가? 우리가 겸손한 자바스크립트에서 다루었던 것들을 돌이켜 보면, 자바스크립트를 꺼 둔 사용자도 사이트의 모든 기능을 이용할 수 있어야 한다. 하지만 우리는 그들 역시 사이트를 즐겁게 - 비록 회전목마로 장식된 문서는 없지만 - 이용할 수 있도록 독특한 스타일링을 주고자 한다.

자, 우리가 원하는 것은 자바스크립트가 사용 가능할 경우에만 활성화되는 스타일시트이다. DOM 스타일시트 인터페이스로 disabled 속성을 사용할 수 있는데, 이것을 응용해서 스타일시트를 켜고 끌 수 있다.

stylesheet 객체의 속성 대부분은 읽기 전용이지만, disabled 같은 일부 속성은 그렇지 않다.

스타일시트의 속성을 사용해서 페이지에 적용된 여러개의 스타일시트를 쉽게 구분할 수 있다. src 속성을 사용하면 외부 스타일시트를 식별할 수 있지만, 내부 스타일시트를 구분하고자 하는 경우에는 쓸모가 없다. 내/외부를 가리지 않고 참조할 수 있는 더 좋은 방법은 title 속성을 사용하는 것이다. document.styleSheets 메서드를 반복해서 적용해 보면, 페이지에 포함된 스타일시트들을 구분해 낼 수 있다. 다음은 이러한 반복의 예 를 보여준다:

function getStyleSheet(unique_title) {
  for(var i=0; i<document.styleSheets.length; i++) {
    var sheet = document.styleSheets[i];
    if(sheet.title == unique_title) {
      return sheet;
    }
  }
}

styleSheets 배열에서 뽑아 낸 각각의 stylesheet 객체에 대해서 그 title 속성을 점검하고, 우리의 코드에서 필요로 하는 title과 일치하는지 확인해 볼 수 있다. 다음 섹션에서 다룰, 규칙을 추가하거나 제거하는 예제에서 이 예제의 실제 동작을 볼 수 있을것이다.

사용자의 기호에 맞게끔 여러개의 스타일시트 중에서 하나를 선택하게 하는 것은 웹 사이트의 아주 일반적인 기능이다 - 여태까지 이야기 해 온 것들을 사용해서, 여러개의 스타일시트를 작성하고 현재 방문자가 선호하는 스타일시트만이 적용되도록 할 수 있다. 자, 실제 예제를 보자 - 텍스트는 처음 로딩시에 스타일을 갖고 있는데, disabled 속성을 true 로 바꿔줌으로서 정의된 CSS가 비활성화된다. disabled 속성을 false 로 전환해서, 쉽게 스타일을 다시 활성화시킬 수 있다. 스타일시트 속성 예제를 참고해서 이것이 실제 동작하는 모습을 확인해 볼 수 있다.

규칙을 추가하거나 제거하기

위에서 언급했던 이론적 사이트에 대해 기억하는가? 이 사이트가 글의 목록을 갖고 있다고 하자. 일부는 CSS에 관한 것이고, 일부는 HTML에 관한 것이며, 일부는 자바스크립트에 관한 것이다. 우리의 웹 페이지에서는 모든 글을 보여주고 있는데, 사용자 중 누군가는 CSS에 관한 글들만 보고 싶어한다. 자, 어떻게 할 것인가? 이미 모든 글이 출력되어 있기 때문에, 다시 서버로 가서 CSS에 관한 글들만 포함하는 페이지를 받아 올 필요는 없다. 이건 시간낭비이다.

이러한 문제를 해결하기 위해, 두가지 자바스크립트를 사용할 수 있는데, 모든 글에 대해 반복처리를 해서 CSS에 관한 글 만 보이게끔 하는 방법(이러한 방법에 대해서는 이후에 다룰 것이다)이 있고, 우리의 스타일시트 중 하나에 규칙을 추가해서 CSS에 관련된 글만 보이게끔 할 수도 있다. CSS를 이용해서 처리하는 것이 모든 요소들 사이를 헤집고 다녀야 하는 전자의 방법보다 빠를 것이다.

stylesheet 객체의 속성 중 두가지를, 이 문제의 해결을 위해 이용할 수 있다. 첫번째 것은 insertRule 함수인데, 이런 식으로 사용한다:

stylesheet.insertRule(rule,index)

rule 는 우리가 스타일시트에 추가하고자 하는 규칙을 담고 있는 문자열이다. index 는 우리가 추가하고자 하는 규칙이 스타일시트의 규칙목록 중 어디에 포함되어야할 지를 명시한다. 실제 사용은 이런식이다.

stylesheet.insertRule(".have-border { border: 1px solid black;}", 0);

insertRule 함수가 어떻게 동작하는지 보여주는 예제를 하나 만들어 보았다. 이 예제에는 스타일시트에서 사용하는 모든 규칙의 목록이 있다. 버튼을 누르면, 스타일시트의 2번 인덱스를 가진 규칙을 추가하고, 이 규칙은 p { … } 에 color: red 을 추가하여 텍스트를 붉은색으로 만든다. 규칙을 추가하고 빼는 예제를 한 번 보길 권한다.

이 규칙을 없애고 싶다면 stylesheet.deleteRule(index) 함수를 사용하면 되는데, 여기에서 index는 지우고자 하는 규칙의 번호이다.

위에서 들었던 예제(회전목마)에서는, HTML과 자바스크립트에 관한 모든 글 들에 대해서 디스플레이 속성을 none 으로 바꿀 수 있다 - 이것이 실제로 어떻게 보이는지 보려면 회전목마 예제를 보기 바란다.

IE는 규칙의 추가/제거 에 대해서 표준을 따르지 않고 있다 - cssRules 대신에 rules 를 사용하고 있으며, deleteRule → removeRule, insertRule → addRule(selector, rule, index) 와 같은 형태로 다르게 사용하고 있다.

요소의 스타일 변경하기

자 여기까지 왔으니, 페이지와 연계된 스타일시트를 어떻게 편집하고, 스타일시트 내에서 CSS 규칙을 어떻게 만들어내고 수정하는지 이해했을 것이다. 그렇다면, DOM 트리 내에서 특정한 요소를 바꾸려면 어떻게 할 것인가? DOM API를 이용해서, 페이지의 특정한 요소에 접근할 수 있다. 우리의 회전목마 예제를 돌이켜보면, 사이트의 기능은 글을 클릭하면 그 글이 강조되고 다른 글들은 뒤로 물러난다는 것이다.

DOM을 통해, 요소의 스타일을 결정하는 style 객체에 접근할 수 있다. 이 style 객체는 CSSStyleDeclaration 으로 정의된다. 이것에 대해 더 자세한 정보를 보고 싶다면 W3C의 CSSStyleDeclaration 인터페이스를 참조하기 바란다. style 객체는 다른 HTML 요소들과는 좀 다르게 동작한다. element.href 이나 element.id 들이 문자열을 반환하는 것과는 다르게, element.style 는 객체를 반환한다. 즉, element.style 에게 문자열을 할당해서 요소의 스타일을 설정할 수는 없다는 뜻이다.

style 객체는 우리가 설정하는 서로 다른 CSS 속성들에 대응하는 속성들을 가지고 있다. 예를 들어, style.color 는 요소에 설정된 색상을 반환한다. element.style.color = “red”; 를 호출함으로써 스타일의 변화를 동적으로 적용할 수 있다. 다음은 요소의 id 를 이용해 색상을 붉은색으로 바꾸는 예제이다.

function colorElementRed(id) {
  var el = document.getElementById(id);
  el.style.color = "red";
}

setAttribute(key, value) 를 사용해도 요소의 스타일을 설정할 수 있다. 예를 들어, element.setAttribute('style', 'color: red'); 와 같이 호출해서 요소에 붉은색을 설정할 수 있지만, 이렇게 하면 당신이 style 객체에 가한 모든 변화들을 지울 것임을 유의하기 바란다.

이런 방식으로 스타일을 설정하는 것은, HTML 의 style 속성에서 선언한 것과 같은 효과를 가진다. 스타일은, 규칙의 중요도와 구체성specificity 값이 해당 요소에 적용되는 다른 규칙보다 우선순위일 때에만 적용된다(구체성에 대한 자세한 설명은 CSS의 상속성과 캐스케이드 글을 읽어보기 바란다).

이런 방식으로 적용하는 CSS 속성이 하이픈을 갖고 있다면 어떻게 될 지 궁금해할지도 모르겠다. 자바스크립트의 동작방식 때문에, 여기에는 조금 다른 문법이 적용된다. 예를 들어, element.style.font-size 와 같이 호출한다면, 자바스크립트는 element.style.font객체에서 size속성 을 가져오려고 시도할 것이고, 이것은 우리가 원하는 바가 아니다. 이러한 문제를 방지하기 위해서, 모든 CSS 속성들은 낙타 표기법CamelCase 으로 작성된다. 아래에 보이는 것과 같이, 요소의 글꼴 크기에 접근하려면 element.style.fontSize 와 같이 써야 한다. 예를 들어:

function changeElement(id) {
  var el = document.getElementById(id);
  el.style.color = "red";
  el.style.fontSize = "15px";
  el.style.backgroundColor = "#FFFFFF";
}

역주 : 낙타 표기법이란, 문구가 몇 개의 단어로 구성되었을 때 단어들 전부를 소문자로 쓰되, 각 단어의 첫글자만 대문자로 표기 - 하지만 문구의 첫 글자는 소문자로 - 함으로서 구별하는 방식을 말한다. 따라서 border-top-width는 borderTopWidth가 되고, first-child는 firstChild, z-index는 zIndex 가 된다.

스타일시트 객체를 기억하는가? styleSheet.cssRules 와 같이 호출하면, 스타일시트에 포함된, CSS 규칙을 표현하는 모든 style 객체의 목록을 반환할 것이다. 이러한 style 객체들은 다른 요소들의 style 객체와 똑같이 사용할 수 있다. 이 경우, 페이지의 특정한 요소가 바뀌는 것이 아니라 CSS 규칙이 적용되는 해당 문서의 모든 요소들이 변경될 것이다.

아래의 예제에서, 텍스트의 크기를 키우는 함수는 style 객체를 사용하고 있고, 텍스트의 크기를 줄이는 함수는 setAttribute 를 이용하고 있다. 만약 당신이 텍스트 색상을 붉은색으로 설정한 뒤 다시 크기를 줄이기 위해 setAttribute 를 호출한다면, 이미 가했던 변경이 엎어써지는 것을 확인할 수 있을 것이다. 요소의 스타일을 변경하는 예제에서 직접 확인해보기 바란다.

요소의 클래스명

요소의 스타일을 바꾸는 다른 방법은, 그것의 class 속성을 바꾸는 것이다. class 는 자바스크립트에서 사용하는 예약어이므로, 요소의 class에 접근하려면 element.className를 사용해야 한다. 요소에 클래스를 설정하거나, 혹은 이미 있던 클래스를 덮어쓰기 위해 element.className 를 사용할 수 있다. 요소의 클래스 이름 예제에서 확인해보기 바란다.

요약

문서에 적용된 스타일을 동적으로 변경하는 방법을 알게 되면, 페이지를 현대적이고 풍부한 상호작용성을 갖추게 하는데에 대단히 유용하다. 이 글에서 소개된 지식들은 자바스크립트 애니메이션 과 같은 좀 더 발전된 테크닉의 기초가 될 것이다. 다만, 문서의 스타일을 변경하는 것은 책임감을 가져야 하며, 남용되어서는 안된다. 우리가 위에서 이미 다룬 바와 같이, 스타일 변경은 또한 사용자의 웹 경험을 좀 더 효율적으로 만들어 준다 - 특정한 상황에서는, 내용을 숨기고/감추고 함으로서 서버와 무의미한 데이터 전송을 하지 않게끔 할 수 있다.

연습문제

  • 스타일을 적용하는데 있어, setAttribute를 사용하는 것과 CSSStyleDeclaration 객체를 사용하는 것의 차이는 무엇인가?
  • 사용자가 버튼을 클릭했을때, 모든 이미지에 녹색 테두리를 적용하는 두가지 방법을 적어보라.
  • 요소의 CSSStyleDeclaration 객체를 변경하면, 항상 요소의 스타일이 바뀔까? 왜 그런가?
  • 특정 스타일시트에 접근할 수 있는 방법을 두가지 적어보라.

저자에 대하여

 Picture of the article author Greg Schechter

Greg Schechter는 웹 산업에 진출한지 얼마 되지 않았다 - 그는 현재 일리노이 대학에서 컴퓨터 과학을 공부하고 있다. 그는 웹 산업에 진출하여 풍부하고 유용한 웹사이트를 만들 꿈에 부풀어있다. Greg에 관해 더 알고 싶다면 GregTheBusker.com을 방문해보기 바란다.

'html / javascript / css' 카테고리의 다른 글

IE 버젼별 적용  (0) 2009.11.18
구버젼 ie 백그라운드 캐쉬  (4) 2009.11.16
zoom 스타일 제어시 Flash Object가 사라지는 버그  (3) 2009.11.13
로봇 배제 표준 (robots.txt)  (0) 2009.10.09
레이아웃 고정  (0) 2009.10.07