미디어위키:Common.js: 두 판 사이의 차이

편집 요약 없음
(내용을 "$(document).ready(function() { var toggleBtns = $('.toggleBtn'); toggleBtns.each(function() { var toggleId = $(this).attr("id"); var contentId = toggleId.replace("toggle", "collapsible"); var content = $("#" + contentId); var toggleTextId = toggleId.replace("toggle", "toggleText"); var toggleText = $("#" + toggleTextId); content.hide(); $(this).click(function() { content...."(으)로 바꿈)
태그: 대체됨
1번째 줄: 1번째 줄:
/**
$(document).ready(function() {
* MediaWiki 커스텀 펼치기/접기 슬라이드 애니메이션
    var toggleBtns = $('.toggleBtn');
*
    toggleBtns.each(function() {
* 같은 mw-customcollapsible-XXX ID가 2개 존재하는 틀 지원
        var toggleId = $(this).attr("id");
*
        var contentId = toggleId.replace("toggle", "collapsible");
* 예:
        var content = $("#" + contentId);
*
        var toggleTextId = toggleId.replace("toggle", "toggleText");
* [첫 번째]
        var toggleText = $("#" + toggleTextId);
* <div id="mw-customcollapsible-XXX">
       
*    <div class="mw-customtoggle-XXX">[ 버튼 ]</div>
        content.hide();
* </div>
       
*
        $(this).click(function() {
* [두 번째]
            content.slideToggle(300);
* <div id="mw-customcollapsible-XXX" class="mw-collapsed">
            if (content.css("display") == "none") {
*    <div class="mw-customtoggle-XXX">[ 버튼 ]</div>
                toggleText.html("[ 펼치기 · 접기 ]");
*    <table>내용</table>
            } else {
* </div>
                toggleText.html("[ 펼치기 · 접기 ]");
*
            }
* 틀 원본 수정 필요 없음.
        });
*/
    });
 
});
( function () {
'use strict';
 
var DURATION = 350;
 
var INITIALIZED = 'data-slide-collapse-ready';
var CONTENT_CLASS = 'mw-slide-collapse-content';
var ANIMATING_CLASS = 'mw-slide-collapse-animating';
 
 
/* --------------------------------
* mw-customtoggle-XXX 에서 XXX 추출
* -------------------------------- */
 
function getName( toggle ) {
var prefix = 'mw-customtoggle-';
var classes = toggle.classList;
var i;
 
for ( i = 0; i < classes.length; i++ ) {
if ( classes[ i ].indexOf( prefix ) === 0 ) {
return classes[ i ].substring(
prefix.length
);
}
}
 
return null;
}
 
 
/* --------------------------------
* 같은 ID를 가진 collapsible 전부 찾기
*
* getElementById() 사용하지 않음!
* -------------------------------- */
 
function getTargets( name ) {
if ( !name ) {
return [];
}
 
var id =
'mw-customcollapsible-' + name;
 
return Array.prototype.slice.call(
document.querySelectorAll(
'[id="' +
CSS.escape( id ) +
'"]'
)
);
}
 
 
/* --------------------------------
* 실제 내용이 들어있는 collapsible 찾기
*
* table 등이 들어있는 두 번째 div를 선택
* -------------------------------- */
 
function getContentTarget( name ) {
var targets = getTargets( name );
var i;
 
/*
* 버튼 외에 다른 요소가 들어있는 target을 찾는다.
*/
for ( i = 0; i < targets.length; i++ ) {
var target = targets[ i ];
var toggle = target.querySelector(
'[class*="mw-customtoggle-"]'
);
 
if ( !toggle ) {
continue;
}
 
var node = toggle.nextElementSibling;
 
if ( node ) {
return target;
}
}
 
return null;
}
 
 
/* --------------------------------
* 실제 내용 wrapper 생성
*
* table 자체의 높이를 변경하지 않고
* 바깥 DIV 높이를 애니메이션
* -------------------------------- */
 
function prepareContent( target ) {
var existing =
target.querySelector(
':scope > .' + CONTENT_CLASS
);
 
if ( existing ) {
return existing;
}
 
var toggle =
target.querySelector(
':scope > [class*="mw-customtoggle-"]'
);
 
if ( !toggle ) {
return null;
}
 
var firstContent =
toggle.nextSibling;
 
if ( !firstContent ) {
return null;
}
 
var wrapper =
document.createElement( 'div' );
 
wrapper.className =
CONTENT_CLASS;
 
/*
* toggle 뒤의 모든 노드를 wrapper로 이동
*/
var node = firstContent;
var nodes = [];
 
while ( node ) {
nodes.push( node );
node = node.nextSibling;
}
 
nodes.forEach( function ( child ) {
wrapper.appendChild( child );
} );
 
target.appendChild( wrapper );
 
return wrapper;
}
 
 
/* --------------------------------
* 같은 이름의 토글들 aria 상태 변경
* -------------------------------- */
 
function setToggleState( name, expanded ) {
var selector =
'.mw-customtoggle-' +
CSS.escape( name );
 
var toggles =
document.querySelectorAll( selector );
 
toggles.forEach( function ( toggle ) {
toggle.setAttribute(
'aria-expanded',
expanded ? 'true' : 'false'
);
} );
}
 
 
/* --------------------------------
* 펼치기
* -------------------------------- */
 
function expand( name, target, content ) {
if (
content.classList.contains(
ANIMATING_CLASS
)
) {
return;
}
 
content.classList.add(
ANIMATING_CLASS
);
 
/*
* MediaWiki 접힌 상태 제거
*/
target.classList.remove(
'mw-collapsed'
);
 
/*
* 부모 자체가 display:none 된 경우 복구
*/
target.style.display =
target.dataset.originalDisplay ||
'inline-block';
 
/*
* 내용 표시
*/
content.style.display = 'block';
content.style.overflow = 'hidden';
 
/*
* 자연 높이를 먼저 계산
*/
content.style.height = 'auto';
 
var endHeight =
content.scrollHeight;
 
/*
* 시작 높이
*/
content.style.height = '0px';
 
/*
* 0px 상태를 브라우저에 확정
*/
content.offsetHeight;
 
content.style.transition =
'height ' +
DURATION +
'ms cubic-bezier(0.4, 0, 0.2, 1)';
 
requestAnimationFrame(
function () {
content.style.height =
endHeight + 'px';
}
);
 
setToggleState(
name,
true
);
 
window.setTimeout(
function () {
content.style.transition = '';
content.style.height = '';
content.style.overflow = '';
content.style.display = '';
 
content.classList.remove(
ANIMATING_CLASS
);
},
DURATION + 30
);
}
 
 
/* --------------------------------
* 접기
* -------------------------------- */
 
function collapse( name, target, content ) {
if (
content.classList.contains(
ANIMATING_CLASS
)
) {
return;
}
 
content.classList.add(
ANIMATING_CLASS
);
 
var startHeight =
content.getBoundingClientRect()
.height;
 
content.style.display = 'block';
content.style.overflow = 'hidden';
 
content.style.height =
startHeight + 'px';
 
/*
* 현재 높이 확정
*/
content.offsetHeight;
 
content.style.transition =
'height ' +
DURATION +
'ms cubic-bezier(0.4, 0, 0.2, 1)';
 
requestAnimationFrame(
function () {
content.style.height =
'0px';
}
);
 
setToggleState(
name,
false
);
 
window.setTimeout(
function () {
content.style.transition = '';
content.style.height = '';
content.style.overflow = '';
content.style.display = 'none';
 
target.classList.add(
'mw-collapsed'
);
 
content.classList.remove(
ANIMATING_CLASS
);
},
DURATION + 30
);
}
 
 
/* --------------------------------
* 현재 열린 상태인지 검사
* -------------------------------- */
 
function isOpen( target, content ) {
if (
target.classList.contains(
'mw-collapsed'
)
) {
return false;
}
 
if (
window.getComputedStyle(
content
).display === 'none'
) {
return false;
}
 
return true;
}
 
 
/* --------------------------------
* 버튼 클릭
* -------------------------------- */
 
function handleToggle( event ) {
var toggle =
event.target.closest(
'[class*="mw-customtoggle-"]'
);
 
if ( !toggle ) {
return;
}
 
var name =
getName( toggle );
 
if ( !name ) {
return;
}
 
var target =
getContentTarget( name );
 
/*
* 우리가 처리할 수 없는 구조면
* MediaWiki 기본 기능을 그대로 사용
*/
if ( !target ) {
return;
}
 
var content =
prepareContent( target );
 
if ( !content ) {
return;
}
 
/*
* 여기까지 왔을 때만
* MediaWiki 기본 즉시 토글 차단
*/
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
 
if (
isOpen(
target,
content
)
) {
collapse(
name,
target,
content
);
} else {
expand(
name,
target,
content
);
}
}
 
 
/* --------------------------------
* 초기 설정
* -------------------------------- */
 
function initialize() {
var toggles =
document.querySelectorAll(
'[class*="mw-customtoggle-"]'
);
 
toggles.forEach(
function ( toggle ) {
if (
toggle.hasAttribute(
INITIALIZED
)
) {
return;
}
 
var name =
getName( toggle );
 
if ( !name ) {
return;
}
 
var target =
getContentTarget(
name
);
 
if ( !target ) {
return;
}
 
var content =
prepareContent(
target
);
 
if ( !content ) {
return;
}
 
/*
* 원래 display 기억
*/
if (
!target.dataset.originalDisplay
) {
target.dataset.originalDisplay =
target.style.display ||
'inline-block';
}
 
/*
* 원래 mw-collapsed인 경우
* 내용만 숨긴다.
*
* 버튼 부모 전체를 숨기지 않음.
*/
if (
target.classList.contains(
'mw-collapsed'
)
) {
content.style.display =
'none';
 
setToggleState(
name,
false
);
}
 
toggle.setAttribute(
INITIALIZED,
'1'
);
}
);
}
 
 
/* --------------------------------
* 클릭 이벤트
*
* capture에서 처리하되,
* 실제 target을 찾은 경우에만
* MediaWiki 기본 이벤트 차단
* -------------------------------- */
 
document.addEventListener(
'click',
handleToggle,
true
);
 
 
/* --------------------------------
* 키보드
* -------------------------------- */
 
document.addEventListener(
'keydown',
function ( event ) {
if (
event.key !== 'Enter' &&
event.key !== ' '
) {
return;
}
 
handleToggle( event );
},
true
);
 
 
/* --------------------------------
* 실행
* -------------------------------- */
 
if (
document.readyState === 'loading'
) {
document.addEventListener(
'DOMContentLoaded',
initialize
);
} else {
initialize();
}
 
 
/*
* MediaWiki 동적 로딩 대응
*/
if (
window.mw &&
mw.hook
) {
mw.hook(
'wikipage.content'
).add(
function () {
initialize();
}
);
}
 
}() );

2026년 8월 20일 (목) 13:40 판

$(document).ready(function() {
    var toggleBtns = $('.toggleBtn');
    toggleBtns.each(function() {
        var toggleId = $(this).attr("id");
        var contentId = toggleId.replace("toggle", "collapsible");
        var content = $("#" + contentId);
        var toggleTextId = toggleId.replace("toggle", "toggleText");
        var toggleText = $("#" + toggleTextId);
        
        content.hide();
        
        $(this).click(function() {
            content.slideToggle(300);
            if (content.css("display") == "none") {
                toggleText.html("[ 펼치기 · 접기 ]");
            } else {
                toggleText.html("[ 펼치기 · 접기 ]");
            }
        });
    });
});