CSS 전후 문자 선택자 사용시에 주의해야 할 점과 우회법

【MV】OxT「Clattanoia」Music Clip フルサイズ

<!DOCTYPE html>

<html>

    <head>

        <title>CSS3 Selector Basic Page</title>

        <style>

            p{counter-increment: rint;}

            p::before{content: counter(rint) ".";} <!--카운더 설정-->

            p::after{content: " - " attr(data-page) " page";}

            p::first-letter{font-size: 3em;}

        </style>

    </head>

    <body>

        <h1>Lorem ipsum dolor sit amet</h1>

        <p data-page = "52">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>

        <p data-page = "273">Lorem ipsum dolor sit amet, consectetur adipscing elit.</p>

    </body>

</html>

와 같은 코드를 실행하게 되면 <p>태그의 각 행의 앞의 부분에 카운터인 rint에 할당된 숫자가 insert되고 그 숫자가 3배 커진다. 따라서

와 같은 출력을 기대하는데

실제로 저 코드를 html 코드로 바꿔서 크롬브라우저에서 열어보면

안된다.

이따구로 뜬다.

그래서 찾아보니 시중에 있는 브라우저(Chrome, Firefox)는 CSS 표준을 잘 지키지 못하거나 구현이 미완성된 부분이 있다고 한다.

따라서 JS를 사용해서 따로 변경해줘야 한다.

<!DOCTYPE html>
<html>
    <head>
        <title>CSS3 Selector Basic Page</title>
        <style>
            p {
                counter-increment: rint;
            }
            p::before {
                content: counter(rint) ". ";
                
            }
            p::first-letter {
                font-size: 3em;
            }
        </style>
    </head>
    <body>
        <h1>Lorem ipsum dolor sit amet</h1>
        <p data-page="52">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
        <p data-page="273">Lorem ipsum dolor sit amet, consectetur adipscing elit.</p>

        <script>
            // 페이지의 모든 내용이 로드된 후 스크립트 실행
            document.addEventListener('DOMContentLoaded', (event) => {
                // 'data-page' 속성을 가진 모든 <p> 요소를 선택
                const paragraphs = document.querySelectorAll('p[data-page]');
                
                // 각 <p> 요소에 대해 반복 작업 수행
                paragraphs.forEach(p => {
                    // 'data-page' 속성 값 가져오기
                    const pageValue = p.getAttribute('data-page');
                    
                    // 새로운 텍스트 노드를 만들어 <p> 요소의 자식으로 추가
                    const textNode = document.createTextNode(` - ${pageValue} page`);
                    p.appendChild(textNode);
                });
            });
        </script>
    </body>
</html>

나도 JS는 잘 모르지만 일단 학습을 위해 다음을 정리한다.

document는 DOM 트리의 최상위 객체이다. 전역객체이다.

브라우저는 HTML 문서를 로드하기 전에 document 객체를 먼저 만든다.

그리고 그 document 객체를 뿌리로 하는 DOM 트리를 만든다.

document 객체의 역할은 다음과 같다.

-프로퍼티로 HTML 문서의 전반적 속성을 나타냄.

메소드로 DOM 객체 생성

메소드로 새로운 DOM 객체 생성

메소드로 HTML 문서의 전반적 제어 지원

등의 일을 하는 모양이다.

document.addEventListener('DOMContentLoaded', (event) => { ... });

DOMConentLoaded 이벤트는 HTML 문서의 모든 콘텐츠(HTML 요소, 텍스트 등) 가 브라우저에 의해 파싱되고 DOM 트리가 완성되었을 때 발생한다. 이 이벤트는 이미지, 스타일시트 등의 외부 리소스가 로드되기 전에 실행되므로, DOM 조작을 시작하기에 적합한 시점이다.

const paragraphs = document.querySelectorAll('p[data-page]');

querySelectorAll은 CSS 선택자를 사용해 DOM에서 일치하는 모든 요소를 찾아 NodeList로 반환한다.

NodeList란? NodeList 는 배열과 유사한 객체로, forEach같은 메서드를 지원하지만 와전한 배열은 아니다.

필요하면 Array.from(paragraphs)로 배열로 변환할 수 있다.

paragraphs.forEach(p => {
    const pageValue = p.getAttribute('data-page');
    const textNode = document.createTextNode(` - ${pageValue} page`);
    p.appendChild(textNode);
});

이 부분은 data-page 속성 값을 가져온다. getAtrribuet(‘data-page’);

해당 값을 통해 텍스트 노드를 생성한다.(예: – 52 page)

텍스트 노드를 <p> 요소의 자식으로 추가한다.

그리고 (event) 는 addEventListener가 호출될 때 브라우저가 자동으로 전달하는 이벤트 객체(Event Object)이다. 즉, 이벤트가 발생했을 때 브라우저가 해당 이벤트에 대한 정보를 담은 객체를 콜백 함수(이벤트 핸들러)에게 전달한다.

event 객체는 발생한 이벤트의 세부 정보를 제공한다. 예를 들어, 어떤 요소에서 이벤트가 발생했는지, 이벤트의 유형은 무엇인지, 추가적인 메타데이터(예: 마우스 클릭 위치, 키보드 입력 값 등)를 포함할 수 있다.

이 코드에서는 event 객체는 실질적으로 사용되지 않았다.

이 코드에서는 event 매게변수를 선언했지만, 내부 로직에서는 event의 속성을 전혀 참조하지 않으므로 불필요하다.

<!DOCTYPE html>
<html>
    <head>
        <title>Click Coordinates</title>
    </head>
    <body>
        <p id="click-info">여기를 클릭하세요.</p>
        <script>
            document.addEventListener('click', (event) => {
                const infoParagraph = document.getElementById('click-info');
                infoParagraph.textContent = `클릭된 좌표: X=${event.clientX}, Y=${event.clientY}`;
            });
        </script>
    </body>
</html>

다음과 같은 코드에서는 더 적극적으로 사용된다.

이제 -> {}란 무엇인지 알아본다.

=> : 이는 화살표 함수라고 한다. 함수 표현식에 대한 간결한 대안으로, 약간의 의미적 차이와 의도적인 사용상의 제한을 가지고 있다.

1.화살표 함수에는 자체 바인딩이 this에 없으며, 인수 또는 super로 사용해야 하며, 메서드로 사용하면 안된다.

2. 화살표 함수는 생성자로 사용할 수 없다. new로 호출하면 TypeError가 반환된다. new.target 키워드에 대한 엑세스 권한도 없다.

3. 화살표 함수는 함수 내부에서 yield를 사용할 수 없으며 제너레이터 함수로 생성할 수 없다.

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/Arrow_functions

this란 무엇인가?

javascript 에서 this는 함수가 호출될 때 그 함수가 어떤 문맥(context)에서 실행되는지를 나타내는 특수한 키워드이다. 즉, this는 함수가 호출되는 상황에 따라 동적으로 결정된다.

전역 스코프에서 this는 브라우저에서는 windows 객체를 가리킨다.

객체 메서드 내에서 this는 그 메서드를 호출한 객체를 가리킴.

const obj = {
    name: "대충 아무 문장입니다.",
    sayName: function() {
        console.log(this.name); // this는 obj를 가리킴
    }
};
obj.sayName(); // 출력: "대충 아무 문장입니다."

따라서 “자체 바인딩이 this에 없다”라는 말을 쉽게 풀어서 설명하자면

화살표 함수는 this를 따로 만들지 않고, 함수가 정의된 곳의 외부 스코프에 있는 this를 그대로 물려받는다.

const obj = {
    name: "메롱",
    sayName: () => {
        console.log(this.name); // this는 obj가 아니라 상위 스코프(window)를 가리킴
    },
    delayedSayName: function() {
        setTimeout(() => {
            console.log(this.name); // this는 obj를 가리킴
        }, 1000);
    }
};
obj.sayName(); // 출력: undefined
obj.delayedSayName(); // 출력: "메롱" (1초 후)

인수 또는 supe로 사용해야 한다는 의미는

인수(arguments): 화살표 함수는 자체적인 arguments 객체를 가지지 않는다. 대신, 상위 스코프의 arguments를 사용한다. 이는 화살표 함수가 콜백 함수로 사용될 때 유용하다.

function outer() {
    const inner = () => {
        console.log(arguments); // outer의 arguments 출력
        console.log(Array.from(arguments)); // 배열로 변환
    };
    inner(1, 2, 3);
}
outer("a", "b");

arguments 객체란?

javascript에서 arguments 는 함수 내에서 자동으로 제공되는 유사 배열 객체(array-like object)이다.

함수에 전달된 모든 인수를 포함하며, 배열과 비슷하지만 완전한 배열은 아니다.

예: arguments는 lenght 속성을 가지며, arguments[0], arguments[1] 처럼 인덱스로 접근 가능하지만, map, forEach 같은 배열 메서드는 사용할 수 없다.

MDN 문서에서 언급하듯이, 화살표함수는 자체적인 arguments 객체를 가지지 않는다. 이는 화살표 함수가 렉시컬 스코프(lexical scope)를 기반으로 동작하기 떄문이다. 주요 특성은 다음과 같다.

  1. 화살표 함수는 this, arguments, super 등을 자체적으로 바인딩하지 않고, 상위 스코프에서 가져온다.
  2. 따라서 화살표 함수 내에서 arguments를 사용하면 상위 함수의 arguments를 참조한다.

렉시컬 스코프란?

함수를 어디서 호출하는가가 아니라 어디에 선언하였는지에 따라 결정되는 것을 의미한다.

즉, 함수를 어디서 선언했는지에 따라 상위 스코프를 결정한다는 뜻이며, 가장 중요한 점은 함수의 호출이 아니라 함수의 선언에 따라 결정된다는 점이다.

자바스크립트는 렉시컬 스코프를 따르므로 함수를 선언한 시점에 상위 스코프가 결정된다.

즉 , 이 말은 함수를 어디에서 호출하였는지는 스코프 결정에 아무런 의미를 주지 않는다는 말이다.