메뉴 여닫기
환경 설정 메뉴 여닫기
개인 메뉴 여닫기
로그인하지 않음
만약 지금 편집한다면 당신의 IP 주소가 공개될 수 있습니다.

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

novawiki
편집 요약 없음
편집 요약 없음
 
(같은 사용자의 중간 판 하나는 보이지 않습니다)
1번째 줄: 1번째 줄:
/* 이 자바스크립트 설정은 모든 문서, 모든 사용자에게 적용됩니다. */
/* 이 자바스크립트 설정은 모든 문서, 모든 사용자에게 적용됩니다. */


/*Autofocus on when clicking search button*/
(function () {
(function () {
   const DETAILS_ID = 'citizen-search-details';
   const DETAILS_ID = 'citizen-search-details';
   const TOGGLE_SEL = '#citizen-search-details > summary.citizen-dropdown-summary';
   const TOGGLE_SEL = '#citizen-search-details > summary.citizen-dropdown-summary';
   const INPUT_ID = 'searchInput';
   const REAL_INPUT_ID = 'searchInput';


  // 화면에 디버그 패널 띄우기 (모바일에서도 보이게)
   function ensureProxy() {
   function panel() {
     let p = document.getElementById('mw-search-proxy');
     let el = document.getElementById('nova-search-debug');
     if (p) return p;
     if (el) return el;
    el = document.createElement('div');
    el.id = 'nova-search-debug';
    el.style.cssText =
      'position:fixed;left:8px;right:8px;bottom:8px;z-index:999999;' +
      'padding:10px;border-radius:12px;background:rgba(0,0,0,.85);' +
      'color:#fff;font:12px/1.4 -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Arial;' +
      'white-space:pre-wrap;max-height:40vh;overflow:auto;';
    el.textContent = '[search debug] ready\n';
    document.documentElement.appendChild(el);
    return el;
  }
 
  function log(msg) {
    const el = panel();
    const t = new Date().toISOString().slice(11, 19);
    el.textContent += `${t}  ${msg}\n`;
    el.scrollTop = el.scrollHeight;
  }
 
  function state(tag) {
    const details = document.getElementById(DETAILS_ID);
    const input = document.getElementById(INPUT_ID);


     const open = details ? !!details.open : null;
     p = document.createElement('input');
     const inputExists = !!input;
     p.id = 'mw-search-proxy';
    p.type = 'search';
    p.autocomplete = 'off';
    p.setAttribute('aria-hidden', 'true');


     let vis = null, dis = null, rect = null, ae = null;
     // "안 보이지만" 포커스는 가능한 상태로 (display:none / visibility:hidden 쓰면 포커스 자체가 막힘)
     if (input) {
    p.style.position = 'fixed';
      const r = input.getBoundingClientRect();
    p.style.left = '-9999px';
      rect = `${Math.round(r.width)}x${Math.round(r.height)}`;
     p.style.top = '0';
      dis = !!input.disabled;
    p.style.width = '1px';
      vis = !!(r.width > 0 && r.height > 0);
    p.style.height = '1px';
     }
    p.style.opacity = '0';
    ae = document.activeElement ? (document.activeElement.id || document.activeElement.tagName) : null;
     p.style.pointerEvents = 'none';


     log(`${tag} | details.open=${open} | input=${inputExists} vis=${vis} rect=${rect} disabled=${dis} | active=${ae}`);
     document.documentElement.appendChild(p);
    return p;
   }
   }


   function tryFocus(where) {
   function focusRealSoon() {
     const details = document.getElementById(DETAILS_ID);
     const details = document.getElementById(DETAILS_ID);
     const input = document.getElementById(INPUT_ID);
     const real = document.getElementById(REAL_INPUT_ID);
 
     if (!details || !real) return;
    state(`before focus (${where})`);
 
     if (!details) { log('NO details element'); return; }
    if (!input) { log('NO input element'); return; }


     // 열려있지 않으면 강제로 열기
     // 열기 보장
     if (!details.open) details.open = true;
     if (!details.open) details.open = true;


     // iOS에서 가끔 필요: 레이아웃 동기화
     // 레이아웃 한 번 강제 (iOS에 은근 도움 됨)
     void input.offsetHeight;
     void real.offsetHeight;


     try {
     // 키보드가 이미 떠있으면 real focus가 더 잘 붙는 편
      input.focus({ preventScroll: true });
    real.focus({ preventScroll: true });
      input.click();
    real.click();
    } catch (e) {
  }
      log('focus threw: ' + e);
    }


     state(`after focus (${where})`);
  function onToggleGesture(e) {
  }
     const hit = e.target.closest(TOGGLE_SEL);
    if (!hit) return;


  // 1) 페이지 로드 자체 확인
    // 1) 유저 제스처 안에서 "input focus"를 먼저 한 번 만들어줌
  log('Common.js loaded');
    const proxy = ensureProxy();
  state('initial');


  // 2) summary 탭 이벤트가 들어오는지 확인 (passive:false 중요)
     try {
  document.addEventListener(
       proxy.focus({ preventScroll: true });
    'touchstart',
       proxy.click();
     function (e) {
    } catch (_) {}
       const hit = !!e.target.closest(TOGGLE_SEL);
      if (!hit) return;
      log('touchstart on toggle ✅ (intercept)');
       e.preventDefault();
      e.stopPropagation();
      tryFocus('touchstart');
    },
    { capture: true, passive: false }
  );


  document.addEventListener(
     // 2) 같은 제스처 안에서 real focus도 바로 시도
     'click',
     focusRealSoon();
     function (e) {
      const hit = !!e.target.closest(TOGGLE_SEL);
      if (!hit) return;
      log('click on toggle ✅ (intercept)');
      e.preventDefault();
      e.stopPropagation();
      tryFocus('click');
    },
    true
  );


  // 3) details가 열리는 순간을 감시 (토글이 우리 코드 말고 다른 경로로 열릴 수도 있으니)
    // 3) 그래도 Citizen이 애니메이션/상태 적용을 늦게 하면 다음 프레임에 한 번 더
  const details = document.getElementById(DETAILS_ID);
     requestAnimationFrame(() => {
  if (details) {
      focusRealSoon();
     const obs = new MutationObserver(() => state('details mutation'));
      // proxy는 필요 없으니 정리
    obs.observe(details, { attributes: true, attributeFilter: ['open'] });
      try { proxy.blur(); } catch (_) {}
    log('observing details[open]');
     });
  } else {
     log('details element not found at load time');
   }
   }
  // iOS에서 제일 잘 먹는 건 touchstart
  document.addEventListener('touchstart', onToggleGesture, { capture: true, passive: true });
  document.addEventListener('click', onToggleGesture, true);
})();
})();

2026년 1월 17일 (토) 19:01 기준 최신판

/* 이 자바스크립트 설정은 모든 문서, 모든 사용자에게 적용됩니다. */

/*Autofocus on when clicking search button*/
(function () {
  const DETAILS_ID = 'citizen-search-details';
  const TOGGLE_SEL = '#citizen-search-details > summary.citizen-dropdown-summary';
  const REAL_INPUT_ID = 'searchInput';

  function ensureProxy() {
    let p = document.getElementById('mw-search-proxy');
    if (p) return p;

    p = document.createElement('input');
    p.id = 'mw-search-proxy';
    p.type = 'search';
    p.autocomplete = 'off';
    p.setAttribute('aria-hidden', 'true');

    // "안 보이지만" 포커스는 가능한 상태로 (display:none / visibility:hidden 쓰면 포커스 자체가 막힘)
    p.style.position = 'fixed';
    p.style.left = '-9999px';
    p.style.top = '0';
    p.style.width = '1px';
    p.style.height = '1px';
    p.style.opacity = '0';
    p.style.pointerEvents = 'none';

    document.documentElement.appendChild(p);
    return p;
  }

  function focusRealSoon() {
    const details = document.getElementById(DETAILS_ID);
    const real = document.getElementById(REAL_INPUT_ID);
    if (!details || !real) return;

    // 열기 보장
    if (!details.open) details.open = true;

    // 레이아웃 한 번 강제 (iOS에 은근 도움 됨)
    void real.offsetHeight;

    // 키보드가 이미 떠있으면 real focus가 더 잘 붙는 편
    real.focus({ preventScroll: true });
    real.click();
  }

  function onToggleGesture(e) {
    const hit = e.target.closest(TOGGLE_SEL);
    if (!hit) return;

    // 1) 유저 제스처 안에서 "input focus"를 먼저 한 번 만들어줌
    const proxy = ensureProxy();

    try {
      proxy.focus({ preventScroll: true });
      proxy.click();
    } catch (_) {}

    // 2) 같은 제스처 안에서 real focus도 바로 시도
    focusRealSoon();

    // 3) 그래도 Citizen이 애니메이션/상태 적용을 늦게 하면 다음 프레임에 한 번 더
    requestAnimationFrame(() => {
      focusRealSoon();
      // proxy는 필요 없으니 정리
      try { proxy.blur(); } catch (_) {}
    });
  }

  // iOS에서 제일 잘 먹는 건 touchstart
  document.addEventListener('touchstart', onToggleGesture, { capture: true, passive: true });
  document.addEventListener('click', onToggleGesture, true);
})();