편집 요약 없음 태그: 되돌려진 기여 |
편집 요약 없음 태그: 되돌려진 기여 |
||
| 1번째 줄: | 1번째 줄: | ||
/** | |||
* MediaWiki 펼치기/접기 애니메이션 | |||
* | |||
* 기존 MediaWiki 토글 동작은 건드리지 않고, | |||
* 실제로 표시/숨김되는 요소에 애니메이션만 추가합니다. | |||
*/ | |||
( function () { | ( function () { | ||
'use strict'; | 'use strict'; | ||
| 4번째 줄: | 11번째 줄: | ||
var DURATION = 300; | var DURATION = 300; | ||
function getToggleName( toggle ) { | |||
var prefix = 'mw-customtoggle-'; | |||
var classes = toggle.classList; | |||
var i; | |||
if ( | for ( i = 0; i < classes.length; i++ ) { | ||
return; | if ( classes[ i ].indexOf( prefix ) === 0 ) { | ||
return classes[ i ].substring( prefix.length ); | |||
} | } | ||
} | |||
return null; | |||
} | |||
function getTarget( name ) { | |||
if ( !name ) { | |||
return null; | |||
} | |||
return document.getElementById( | |||
'mw-customcollapsible-' + name | |||
); | |||
} | |||
/* | |||
* target 자체 안에 toggle만 있는 경우, | |||
* target은 애니메이션 대상으로 사용하지 않습니다. | |||
* | |||
* 실제로 MediaWiki가 숨기고 표시하는 요소를 | |||
* 주변 DOM에서 찾습니다. | |||
*/ | |||
function findAnimatedContent( target, toggle ) { | |||
var content = target.querySelector( | |||
'.mw-collapsible-content' | |||
); | |||
if ( content && !content.contains( toggle ) ) { | |||
return content; | |||
} | |||
/* | |||
* 다음 형제 요소가 실제 내용인 구조 대응 | |||
*/ | |||
var next = target.nextElementSibling; | |||
if ( next && !next.classList.contains( 'mw-customtoggle' ) ) { | |||
return next; | |||
} | |||
var | /* | ||
target. | * 부모 내부에서 target 다음 요소 탐색 | ||
*/ | |||
if ( target.parentElement ) { | |||
var siblings = | |||
Array.prototype.slice.call( | |||
target.parentElement.children | |||
); | |||
var index = siblings.indexOf( target ); | |||
if ( | if ( | ||
index !== -1 && | |||
index + 1 < siblings.length | |||
) { | |||
return siblings[ index + 1 ]; | |||
} | } | ||
} | } | ||
return null; | |||
} | |||
function | function animateOpen( element ) { | ||
if ( !element ) { | |||
return; | |||
} | |||
element.style.overflow = 'hidden'; | |||
element.style.height = 'auto'; | |||
var height = | var height = element.scrollHeight; | ||
element.style.height = '0px'; | |||
element.offsetHeight; | |||
var animation = | var animation = element.animate( | ||
[ | [ | ||
{ height: '0px' }, | { height: '0px' }, | ||
| 92번째 줄: | 109번째 줄: | ||
animation.onfinish = function () { | animation.onfinish = function () { | ||
element.style.height = ''; | |||
element.style.overflow = ''; | |||
animation.cancel(); | animation.cancel(); | ||
| 100번째 줄: | 116번째 줄: | ||
} | } | ||
function | function animateClose( element ) { | ||
if ( !element ) { | |||
return; | |||
} | |||
var height = | var height = | ||
element.getBoundingClientRect().height; | |||
if ( height <= 0 ) { | |||
return; | |||
} | |||
element.style.overflow = 'hidden'; | |||
element.style.height = height + 'px'; | |||
var animation = | var animation = element.animate( | ||
[ | [ | ||
{ height: height + 'px' }, | { height: height + 'px' }, | ||
| 120번째 줄: | 144번째 줄: | ||
animation.onfinish = function () { | animation.onfinish = function () { | ||
element.style.height = ''; | |||
element.style.overflow = ''; | |||
animation.cancel(); | animation.cancel(); | ||
}; | }; | ||
} | } | ||
document.addEventListener( | |||
'click', | |||
function ( event ) { | |||
var toggle = | |||
event.target.closest( | |||
'.mw-customtoggle' | |||
); | |||
if ( !toggle ) { | |||
return; | |||
} | |||
var name = getToggleName( toggle ); | |||
if ( !name ) { | |||
return; | |||
} | |||
var target = getTarget( name ); | |||
if ( !target ) { | |||
return; | |||
} | |||
var content = | |||
findAnimatedContent( | |||
target, | |||
toggle | |||
); | |||
if ( !content ) { | |||
/* | |||
* 내용을 특정할 수 없으면 | |||
* MediaWiki 기본 동작만 실행. | |||
* | |||
* 버튼 자체를 숨기는 일은 하지 않음. | |||
*/ | |||
return; | |||
} | |||
/* | |||
* 여기서는 기본 이벤트를 차단하지 않습니다. | |||
* MediaWiki가 원래대로 펼치고 접게 둡니다. | |||
*/ | |||
var wasHidden = | |||
window.getComputedStyle( content ) | |||
.display === 'none' || | |||
content.getBoundingClientRect() | |||
.height === 0; | |||
/* | |||
* MediaWiki 기본 토글 처리 직후 상태를 읽기 위해 | |||
* 다음 task에서 실행 | |||
*/ | |||
window.setTimeout( | |||
function () { | |||
var isHidden = | |||
window.getComputedStyle( | |||
content | |||
).display === 'none' || | |||
content.getBoundingClientRect() | |||
.height === 0; | |||
if ( wasHidden && !isHidden ) { | |||
animateOpen( content ); | |||
} else if ( | |||
!wasHidden && | |||
isHidden | |||
) { | |||
/* | |||
* 이미 display:none이 되어버린 경우에는 | |||
* 애니메이션할 높이가 없기 때문에 | |||
* 이 구조에서는 접기 애니메이션을 | |||
* 완벽히 재현할 수 없습니다. | |||
*/ | |||
} | |||
}, | |||
0 | |||
); | |||
}, | |||
false | |||
); | |||
}() ); | }() ); | ||
2026년 8월 20일 (목) 13:20 판
/**
* MediaWiki 펼치기/접기 애니메이션
*
* 기존 MediaWiki 토글 동작은 건드리지 않고,
* 실제로 표시/숨김되는 요소에 애니메이션만 추가합니다.
*/
( function () {
'use strict';
var DURATION = 300;
function getToggleName( 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;
}
function getTarget( name ) {
if ( !name ) {
return null;
}
return document.getElementById(
'mw-customcollapsible-' + name
);
}
/*
* target 자체 안에 toggle만 있는 경우,
* target은 애니메이션 대상으로 사용하지 않습니다.
*
* 실제로 MediaWiki가 숨기고 표시하는 요소를
* 주변 DOM에서 찾습니다.
*/
function findAnimatedContent( target, toggle ) {
var content = target.querySelector(
'.mw-collapsible-content'
);
if ( content && !content.contains( toggle ) ) {
return content;
}
/*
* 다음 형제 요소가 실제 내용인 구조 대응
*/
var next = target.nextElementSibling;
if ( next && !next.classList.contains( 'mw-customtoggle' ) ) {
return next;
}
/*
* 부모 내부에서 target 다음 요소 탐색
*/
if ( target.parentElement ) {
var siblings =
Array.prototype.slice.call(
target.parentElement.children
);
var index = siblings.indexOf( target );
if (
index !== -1 &&
index + 1 < siblings.length
) {
return siblings[ index + 1 ];
}
}
return null;
}
function animateOpen( element ) {
if ( !element ) {
return;
}
element.style.overflow = 'hidden';
element.style.height = 'auto';
var height = element.scrollHeight;
element.style.height = '0px';
element.offsetHeight;
var animation = element.animate(
[
{ height: '0px' },
{ height: height + 'px' }
],
{
duration: DURATION,
easing: 'ease',
fill: 'forwards'
}
);
animation.onfinish = function () {
element.style.height = '';
element.style.overflow = '';
animation.cancel();
};
}
function animateClose( element ) {
if ( !element ) {
return;
}
var height =
element.getBoundingClientRect().height;
if ( height <= 0 ) {
return;
}
element.style.overflow = 'hidden';
element.style.height = height + 'px';
var animation = element.animate(
[
{ height: height + 'px' },
{ height: '0px' }
],
{
duration: DURATION,
easing: 'ease',
fill: 'forwards'
}
);
animation.onfinish = function () {
element.style.height = '';
element.style.overflow = '';
animation.cancel();
};
}
document.addEventListener(
'click',
function ( event ) {
var toggle =
event.target.closest(
'.mw-customtoggle'
);
if ( !toggle ) {
return;
}
var name = getToggleName( toggle );
if ( !name ) {
return;
}
var target = getTarget( name );
if ( !target ) {
return;
}
var content =
findAnimatedContent(
target,
toggle
);
if ( !content ) {
/*
* 내용을 특정할 수 없으면
* MediaWiki 기본 동작만 실행.
*
* 버튼 자체를 숨기는 일은 하지 않음.
*/
return;
}
/*
* 여기서는 기본 이벤트를 차단하지 않습니다.
* MediaWiki가 원래대로 펼치고 접게 둡니다.
*/
var wasHidden =
window.getComputedStyle( content )
.display === 'none' ||
content.getBoundingClientRect()
.height === 0;
/*
* MediaWiki 기본 토글 처리 직후 상태를 읽기 위해
* 다음 task에서 실행
*/
window.setTimeout(
function () {
var isHidden =
window.getComputedStyle(
content
).display === 'none' ||
content.getBoundingClientRect()
.height === 0;
if ( wasHidden && !isHidden ) {
animateOpen( content );
} else if (
!wasHidden &&
isHidden
) {
/*
* 이미 display:none이 되어버린 경우에는
* 애니메이션할 높이가 없기 때문에
* 이 구조에서는 접기 애니메이션을
* 완벽히 재현할 수 없습니다.
*/
}
},
0
);
},
false
);
}() );