편집 요약 없음 태그: 되돌려진 기여 |
편집 요약 없음 태그: 수동 되돌리기 |
||
| 1번째 줄: | 1번째 줄: | ||
/** | /** | ||
* MediaWiki | * MediaWiki 커스텀 펼치기/접기 애니메이션 | ||
* | * | ||
* | * 지원 예시: | ||
* | * | ||
* | * mw-customtoggle-KSC_LEADER | ||
* | * ↕ | ||
* mw-customcollapsible-KSC_LEADER | |||
* | |||
* mw-customtoggle-COMPANY | |||
* ↕ | |||
* mw-customcollapsible-COMPANY | |||
* | |||
* 식별자 이름과 관계없이 자동으로 연결합니다. | |||
*/ | */ | ||
| 12번째 줄: | 19번째 줄: | ||
var DURATION = 300; | var DURATION = 300; | ||
var ANIMATING_CLASS = 'mw-custom-collapse-animating'; | |||
/** | /** | ||
* | * 토글 요소에서 식별자 추출 | ||
* | * | ||
* mw-customtoggle-KSC_LEADER | * mw-customtoggle-KSC_LEADER | ||
* → KSC_LEADER | * → KSC_LEADER | ||
*/ | */ | ||
function | function getToggleName( toggle ) { | ||
var classes = toggle. | var classes = toggle.classList; | ||
var i; | var i; | ||
var prefix = 'mw-customtoggle-'; | |||
for ( i = 0; i < classes.length; i++ ) { | for ( i = 0; i < classes.length; i++ ) { | ||
if ( | if ( classes[ i ].indexOf( prefix ) === 0 ) { | ||
return classes[ i ].substring( prefix.length ); | |||
return classes[ i ].substring( | |||
} | } | ||
} | } | ||
| 37번째 줄: | 42번째 줄: | ||
/** | /** | ||
* 연결된 | * CSS 선택자에서 사용할 문자열 이스케이프 | ||
*/ | |||
function escapeSelector( value ) { | |||
if ( window.CSS && CSS.escape ) { | |||
return CSS.escape( value ); | |||
} | |||
return value.replace( | |||
/([!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g, | |||
'\\\\$1' | |||
); | |||
} | |||
/** | |||
* 식별자와 연결된 펼침 대상 찾기 | |||
*/ | */ | ||
function getTarget( | function getTarget( name ) { | ||
if ( ! | if ( !name ) { | ||
return null; | return null; | ||
} | } | ||
/* | /* | ||
* | * class 방식 | ||
* | |||
* class=" | |||
* mw-collapsible | |||
* mw-customcollapsible-KSC_LEADER | |||
* " | |||
*/ | */ | ||
var target = document. | var target = document.querySelector( | ||
'mw-customcollapsible-' + | '.mw-customcollapsible-' + | ||
escapeSelector( name ) | |||
); | ); | ||
| 56번째 줄: | 81번째 줄: | ||
/* | /* | ||
* | * id 방식도 대응 | ||
*/ | */ | ||
target = document. | target = document.getElementById( | ||
' | 'mw-customcollapsible-' + name | ||
); | ); | ||
| 67번째 줄: | 91번째 줄: | ||
/** | /** | ||
* 실제 | * 실제 애니메이션을 적용할 콘텐츠 | ||
*/ | */ | ||
function getContent( target ) { | function getContent( target ) { | ||
| 78번째 줄: | 102번째 줄: | ||
} | } | ||
return target; | return target; | ||
} | } | ||
/** | |||
* 현재 접혀있는지 확인 | |||
*/ | |||
function isCollapsed( target, content ) { | function isCollapsed( target, content ) { | ||
if ( target.classList.contains( 'mw-collapsed' ) ) { | |||
return true; | |||
window.getComputedStyle( content ).display === 'none' | } | ||
return window.getComputedStyle( content ).display === 'none'; | |||
} | } | ||
/** | |||
* 펼치기 | |||
*/ | |||
function expand( target, content, toggle ) { | function expand( target, content, toggle ) { | ||
if ( | if ( content.classList.contains( ANIMATING_CLASS ) ) { | ||
return; | return; | ||
} | } | ||
content.classList.add( ANIMATING_CLASS ); | |||
target.classList.remove( 'mw-collapsed' ); | target.classList.remove( 'mw-collapsed' ); | ||
| 105번째 줄: | 130번째 줄: | ||
content.style.display = 'block'; | content.style.display = 'block'; | ||
content.style.height = 'auto'; | content.style.height = 'auto'; | ||
content.style.overflow = 'hidden'; | |||
var targetHeight = content.scrollHeight; | var targetHeight = content.scrollHeight; | ||
| 111번째 줄: | 137번째 줄: | ||
content.style.opacity = '0'; | content.style.opacity = '0'; | ||
/* | |||
* 강제 reflow | |||
*/ | |||
content.offsetHeight; | content.offsetHeight; | ||
| 137번째 줄: | 161번째 줄: | ||
animation.onfinish = function () { | animation.onfinish = function () { | ||
content.classList.remove( | content.classList.remove( ANIMATING_CLASS ); | ||
content.style.display = ''; | content.style.display = ''; | ||
content.style.height = ''; | content.style.height = ''; | ||
content.style.opacity = ''; | content.style.opacity = ''; | ||
content.style.overflow = ''; | |||
toggle.setAttribute( | toggle.setAttribute( | ||
| 154번째 줄: | 177번째 줄: | ||
} | } | ||
/** | |||
* 접기 | |||
*/ | |||
function collapse( target, content, toggle ) { | function collapse( target, content, toggle ) { | ||
if ( | if ( content.classList.contains( ANIMATING_CLASS ) ) { | ||
return; | return; | ||
} | } | ||
content.classList.add( ANIMATING_CLASS ); | |||
var startHeight = | var startHeight = | ||
| 167번째 줄: | 191번째 줄: | ||
content.style.display = 'block'; | content.style.display = 'block'; | ||
content.style.overflow = 'hidden'; | |||
content. | |||
var animation = content.animate( | var animation = content.animate( | ||
| 193번째 줄: | 214번째 줄: | ||
target.classList.add( 'mw-collapsed' ); | target.classList.add( 'mw-collapsed' ); | ||
content.classList.remove( | content.classList.remove( ANIMATING_CLASS ); | ||
content.style.display = 'none'; | content.style.display = 'none'; | ||
content.style.height = ''; | content.style.height = ''; | ||
content.style.opacity = ''; | content.style.opacity = ''; | ||
content.style.overflow = ''; | |||
toggle.setAttribute( | toggle.setAttribute( | ||
| 210번째 줄: | 230번째 줄: | ||
} | } | ||
function | /** | ||
var | * 토글 실행 | ||
*/ | |||
function toggleCollapsible( toggle ) { | |||
var name = getToggleName( toggle ); | |||
if ( ! | if ( !name ) { | ||
return; | return false; | ||
} | } | ||
var target = getTarget( | var target = getTarget( name ); | ||
if ( !target ) { | if ( !target ) { | ||
return; | return false; | ||
} | } | ||
| 226번째 줄: | 249번째 줄: | ||
if ( !content ) { | if ( !content ) { | ||
return; | return false; | ||
} | } | ||
| 242번째 줄: | 265번째 줄: | ||
); | ); | ||
} | } | ||
return true; | |||
} | } | ||
/* | /** | ||
* MediaWiki 기본 | * 클릭 | ||
* | |||
* capture=true를 사용해서 | |||
* MediaWiki 기본 토글보다 먼저 처리 | |||
*/ | */ | ||
document.addEventListener( | document.addEventListener( | ||
| 258번째 줄: | 286번째 줄: | ||
} | } | ||
if ( ! | if ( !getToggleName( toggle ) ) { | ||
return; | return; | ||
} | } | ||
| 266번째 줄: | 294번째 줄: | ||
event.stopImmediatePropagation(); | event.stopImmediatePropagation(); | ||
toggleCollapsible( toggle ); | |||
}, | }, | ||
true | true | ||
); | ); | ||
/* | /** | ||
* | * Enter / Space 키 지원 | ||
*/ | */ | ||
document.addEventListener( | document.addEventListener( | ||
| 289번째 줄: | 317번째 줄: | ||
if ( !toggle ) { | if ( !toggle ) { | ||
return; | |||
} | |||
if ( !getToggleName( toggle ) ) { | |||
return; | return; | ||
} | } | ||
| 296번째 줄: | 328번째 줄: | ||
event.stopImmediatePropagation(); | event.stopImmediatePropagation(); | ||
toggleCollapsible( toggle ); | |||
}, | }, | ||
true | true | ||
2026년 8월 20일 (목) 13:08 판
/**
* MediaWiki 커스텀 펼치기/접기 애니메이션
*
* 지원 예시:
*
* mw-customtoggle-KSC_LEADER
* ↕
* mw-customcollapsible-KSC_LEADER
*
* mw-customtoggle-COMPANY
* ↕
* mw-customcollapsible-COMPANY
*
* 식별자 이름과 관계없이 자동으로 연결합니다.
*/
( function () {
'use strict';
var DURATION = 300;
var ANIMATING_CLASS = 'mw-custom-collapse-animating';
/**
* 토글 요소에서 식별자 추출
*
* mw-customtoggle-KSC_LEADER
* → KSC_LEADER
*/
function getToggleName( toggle ) {
var classes = toggle.classList;
var i;
var prefix = 'mw-customtoggle-';
for ( i = 0; i < classes.length; i++ ) {
if ( classes[ i ].indexOf( prefix ) === 0 ) {
return classes[ i ].substring( prefix.length );
}
}
return null;
}
/**
* CSS 선택자에서 사용할 문자열 이스케이프
*/
function escapeSelector( value ) {
if ( window.CSS && CSS.escape ) {
return CSS.escape( value );
}
return value.replace(
/([!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g,
'\\\\$1'
);
}
/**
* 식별자와 연결된 펼침 대상 찾기
*/
function getTarget( name ) {
if ( !name ) {
return null;
}
/*
* class 방식
*
* class="
* mw-collapsible
* mw-customcollapsible-KSC_LEADER
* "
*/
var target = document.querySelector(
'.mw-customcollapsible-' +
escapeSelector( name )
);
if ( target ) {
return target;
}
/*
* id 방식도 대응
*/
target = document.getElementById(
'mw-customcollapsible-' + name
);
return target;
}
/**
* 실제 애니메이션을 적용할 콘텐츠
*/
function getContent( target ) {
var content = target.querySelector(
'.mw-collapsible-content'
);
if ( content ) {
return content;
}
return target;
}
/**
* 현재 접혀있는지 확인
*/
function isCollapsed( target, content ) {
if ( target.classList.contains( 'mw-collapsed' ) ) {
return true;
}
return window.getComputedStyle( content ).display === 'none';
}
/**
* 펼치기
*/
function expand( target, content, toggle ) {
if ( content.classList.contains( ANIMATING_CLASS ) ) {
return;
}
content.classList.add( ANIMATING_CLASS );
target.classList.remove( 'mw-collapsed' );
content.style.display = 'block';
content.style.height = 'auto';
content.style.overflow = 'hidden';
var targetHeight = content.scrollHeight;
content.style.height = '0px';
content.style.opacity = '0';
/*
* 강제 reflow
*/
content.offsetHeight;
var animation = content.animate(
[
{
height: '0px',
opacity: 0
},
{
height: targetHeight + 'px',
opacity: 1
}
],
{
duration: DURATION,
easing: 'ease',
fill: 'forwards'
}
);
animation.onfinish = function () {
content.classList.remove( ANIMATING_CLASS );
content.style.display = '';
content.style.height = '';
content.style.opacity = '';
content.style.overflow = '';
toggle.setAttribute(
'aria-expanded',
'true'
);
animation.cancel();
};
}
/**
* 접기
*/
function collapse( target, content, toggle ) {
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';
var animation = content.animate(
[
{
height: startHeight + 'px',
opacity: 1
},
{
height: '0px',
opacity: 0
}
],
{
duration: DURATION,
easing: 'ease',
fill: 'forwards'
}
);
animation.onfinish = function () {
target.classList.add( 'mw-collapsed' );
content.classList.remove( ANIMATING_CLASS );
content.style.display = 'none';
content.style.height = '';
content.style.opacity = '';
content.style.overflow = '';
toggle.setAttribute(
'aria-expanded',
'false'
);
animation.cancel();
};
}
/**
* 토글 실행
*/
function toggleCollapsible( toggle ) {
var name = getToggleName( toggle );
if ( !name ) {
return false;
}
var target = getTarget( name );
if ( !target ) {
return false;
}
var content = getContent( target );
if ( !content ) {
return false;
}
if ( isCollapsed( target, content ) ) {
expand(
target,
content,
toggle
);
} else {
collapse(
target,
content,
toggle
);
}
return true;
}
/**
* 클릭
*
* capture=true를 사용해서
* MediaWiki 기본 토글보다 먼저 처리
*/
document.addEventListener(
'click',
function ( event ) {
var toggle = event.target.closest(
'.mw-customtoggle'
);
if ( !toggle ) {
return;
}
if ( !getToggleName( toggle ) ) {
return;
}
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
toggleCollapsible( toggle );
},
true
);
/**
* Enter / Space 키 지원
*/
document.addEventListener(
'keydown',
function ( event ) {
if (
event.key !== 'Enter' &&
event.key !== ' '
) {
return;
}
var toggle = event.target.closest(
'.mw-customtoggle'
);
if ( !toggle ) {
return;
}
if ( !getToggleName( toggle ) ) {
return;
}
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
toggleCollapsible( toggle );
},
true
);
}() );