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

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

novawiki
편집 요약 없음
편집 요약 없음
1번째 줄: 1번째 줄:
/* 이 자바스크립트 설정은 모든 문서, 모든 사용자에게 적용됩니다. */
/* 이 자바스크립트 설정은 모든 문서, 모든 사용자에게 적용됩니다. */


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


   // 화면에 디버그 패널 띄우기 (모바일에서도 보이게)
   if (!DETAILS) return;
  function panel() {
    let el = document.getElementById('nova-search-debug');
    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) {
   function isVisible(el) {
     const el = panel();
     if (!el) return false;
     const t = new Date().toISOString().slice(11, 19);
     const r = el.getBoundingClientRect();
     el.textContent += `${t}  ${msg}\n`;
     return r.width > 0 && r.height > 0;
    el.scrollTop = el.scrollHeight;
   }
   }


   function state(tag) {
   function focusInput(tag) {
    const details = document.getElementById(DETAILS_ID);
     const input = document.getElementById(INPUT_ID);
     const input = document.getElementById(INPUT_ID);
    if (!input) return false;
    if (!isVisible(input)) return false;


     const open = details ? !!details.open : null;
     // iOS-friendly focus
     const inputExists = !!input;
    input.focus({ preventScroll: true });
     input.click();


     let vis = null, dis = null, rect = null, ae = null;
     return document.activeElement === input;
    if (input) {
      const r = input.getBoundingClientRect();
      rect = `${Math.round(r.width)}x${Math.round(r.height)}`;
      dis = !!input.disabled;
      vis = !!(r.width > 0 && r.height > 0);
    }
    ae = document.activeElement ? (document.activeElement.id || document.activeElement.tagName) : null;
 
    log(`${tag} | details.open=${open} | input=${inputExists} vis=${vis} rect=${rect} disabled=${dis} | active=${ae}`);
   }
   }


   function tryFocus(where) {
   function focusWhenShown() {
     const details = document.getElementById(DETAILS_ID);
     const card = document.getElementById(CARD_ID);
     const input = document.getElementById(INPUT_ID);
     const input = document.getElementById(INPUT_ID);
    if (!card || !input) return;


     state(`before focus (${where})`);
     // 1) 이미 보이면 즉시
    if (focusInput('immediate')) return;


     if (!details) { log('NO details element'); return; }
     // 2) Citizen이 열릴 때 CSS transition/animation 타면 그 끝에서 포커스
    if (!input) { log('NO input element'); return; }
    const onDone = () => {
      if (focusInput('transitionend')) cleanup();
    };


     // 열려있지 않으면 강제로 열기
     const cleanup = () => {
    if (!details.open) details.open = true;
      card.removeEventListener('transitionend', onDone, true);
      card.removeEventListener('animationend', onDone, true);
      clearInterval(poll);
    };


     // iOS에서 가끔 필요: 레이아웃 동기화
     card.addEventListener('transitionend', onDone, true);
     void input.offsetHeight;
     card.addEventListener('animationend', onDone, true);


     try {
     // 3) 그래도 안 잡히면 짧은 폴링 (최대 1초)
      input.focus({ preventScroll: true });
    const start = Date.now();
      input.click();
     const poll = setInterval(() => {
     } catch (e) {
       if (focusInput('poll')) return cleanup();
       log('focus threw: ' + e);
      if (Date.now() - start > 1000) cleanup();
    }
     }, 50);
 
     state(`after focus (${where})`);
   }
   }


   // 1) 페이지 로드 자체 확인
   // 핵심: "사용자 클릭" 이벤트 안에서 열고, 그 다음 '보이는 순간'에 포커스
   log('Common.js loaded');
   function openAndQueueFocus(e) {
  state('initial');
    const hit = e.target.closest(TOGGLE_SEL);
    if (!hit) return;


  // 2) summary 탭 이벤트가 들어오는지 확인 (passive:false 중요)
    // 여기서 preventDefault를 걸면 Citizen 토글 로직이 꼬일 수 있어서
  document.addEventListener(
     // 일단은 open만 보장하고, Citizen이 열어주는 흐름도 같이 허용.
     'touchstart',
     if (!DETAILS.open) DETAILS.open = true;
     function (e) {
      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(
     focusWhenShown();
    'click',
  }
     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가 열리는 순간을 감시 (토글이 우리 코드 말고 다른 경로로 열릴 수도 있으니)
   // iOS: touchstart가 제일 안정적
   const details = document.getElementById(DETAILS_ID);
   document.addEventListener('touchstart', openAndQueueFocus, { capture: true, passive: true });
  if (details) {
   document.addEventListener('click', openAndQueueFocus, true);
    const obs = new MutationObserver(() => state('details mutation'));
    obs.observe(details, { attributes: true, attributeFilter: ['open'] });
    log('observing details[open]');
   } else {
    log('details element not found at load time');
  }
})();
})();

2026년 1월 17일 (토) 18:59 판

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

/*Autofocus on when clicking search button*/
(function () {
  const DETAILS = document.getElementById('citizen-search-details');
  const TOGGLE_SEL = '#citizen-search-details > summary.citizen-dropdown-summary';
  const CARD_ID = 'citizen-search__card';
  const INPUT_ID = 'searchInput';

  if (!DETAILS) return;

  function isVisible(el) {
    if (!el) return false;
    const r = el.getBoundingClientRect();
    return r.width > 0 && r.height > 0;
  }

  function focusInput(tag) {
    const input = document.getElementById(INPUT_ID);
    if (!input) return false;
    if (!isVisible(input)) return false;

    // iOS-friendly focus
    input.focus({ preventScroll: true });
    input.click();

    return document.activeElement === input;
  }

  function focusWhenShown() {
    const card = document.getElementById(CARD_ID);
    const input = document.getElementById(INPUT_ID);
    if (!card || !input) return;

    // 1) 이미 보이면 즉시
    if (focusInput('immediate')) return;

    // 2) Citizen이 열릴 때 CSS transition/animation 타면 그 끝에서 포커스
    const onDone = () => {
      if (focusInput('transitionend')) cleanup();
    };

    const cleanup = () => {
      card.removeEventListener('transitionend', onDone, true);
      card.removeEventListener('animationend', onDone, true);
      clearInterval(poll);
    };

    card.addEventListener('transitionend', onDone, true);
    card.addEventListener('animationend', onDone, true);

    // 3) 그래도 안 잡히면 짧은 폴링 (최대 1초)
    const start = Date.now();
    const poll = setInterval(() => {
      if (focusInput('poll')) return cleanup();
      if (Date.now() - start > 1000) cleanup();
    }, 50);
  }

  // 핵심: "사용자 클릭" 이벤트 안에서 열고, 그 다음 '보이는 순간'에 포커스
  function openAndQueueFocus(e) {
    const hit = e.target.closest(TOGGLE_SEL);
    if (!hit) return;

    // 여기서 preventDefault를 걸면 Citizen 토글 로직이 꼬일 수 있어서
    // 일단은 open만 보장하고, Citizen이 열어주는 흐름도 같이 허용.
    if (!DETAILS.open) DETAILS.open = true;

    focusWhenShown();
  }

  // iOS: touchstart가 제일 안정적
  document.addEventListener('touchstart', openAndQueueFocus, { capture: true, passive: true });
  document.addEventListener('click', openAndQueueFocus, true);
})();