Yeni tarih

Özel Açılır Öğelerin Doğru Görüntülenmesi: Nasıl Yapılır Kılavuzu

ile Andrei Sieedugin6m2025/04/04
Read on Terminal Reader

Çok uzun; Okumak

Açılır liste öğelerinizi kesilmeden her yerde göstermenize yardımcı olan küçük bir numara.
featured image - Özel Açılır Öğelerin Doğru Görüntülenmesi: Nasıl Yapılır Kılavuzu
Andrei Sieedugin HackerNoon profile picture
0-item

Varsayılan HTML bileşenlerini geliştirmek yerine kendi bileşeninizi oluşturduğunuzda hissettiğiniz duyguyu biliyor musunuz? Tasarım ekibiniz güzel bir şey yarattı, ancak tarayıcılar bunu kutudan çıktığı haliyle desteklemiyor ve her yerde düzeltmek bir kabusa dönüşüyor. Hepimiz bu acıyı biliyoruz, ancak bu zorluklar işimizi ilginç hale getiriyor.


Bugün, bu heyecanlı yolculukta bizi bekleyen tuzaklardan birinden bahsetmek istiyorum: Seçim menüleri veya tarih seçiciler gibi açılır öğelerin yerleşimi.

Kesinlikle Yanlış

İlk başta, position: absolute tüm sorunlarımızı çözüyor gibi görünüyor ve bir dereceye kadar da öyle. Ama sonra, modal pencereler her şeyi mahvediyor.



Açılır menü taşarsa kesilir. Elbette aşağı kaydırıp görebilirsiniz, ancak tasarımcınızın size keskin bir nesneyle ulaşamayacağı konusunda dua etmeniz daha iyi olur.


Bu mükemmel olmaktan çok uzak; daha iyisini yapabiliriz.

'fixed' ile düzeltin

İçeriği her şeyin üzerinde görüntülemek istiyorsak, position: fixed ihtiyacımız var. Tek sorun, ebeveyn öğe koordinatlarını kaybedecek olmamız: fixed öğeler doğası gereği oldukça bağımsızdır. Bu, yapmamız gereken tek şeyin bu durumlarda açılır öğenin tam koordinatlarını belirlemek olduğu anlamına gelir:


  • Bunu gösterdiğimizde.
  • İçeriği değiştiğinde.
  • Pencereyi ve/veya kaydırılabilir üst öğeyi kaydırdığımızda.
  • Pencereyi ve/veya kaydırılabilir üst öğeyi yeniden boyutlandırdığımızda.


Ayrıca ekranın altına çok yakınsa toggle'ın üstünde görüntülenip görüntülenmeyeceğine karar vermemiz gerekiyor. Yapılabilir gibi görünüyor.


Ben Vue.js kullanacağım ama React veya Angular'ı tercih etseniz bile takip etmesi kolay olmalı.

Hadi Bu Ortaklığı Sallayalım

Kullanacağımız yapı şu şekilde:

 export const useDropdownAttributes = () => { const dropdownWidth = ref(''); const dropdownTop = ref(''); const dropdownBottom = ref(''); const dropdownLeft = ref(''); const isDirectedUpwards = ref(false); const togglerRect = ref<DOMRect>(); const dropdownRect = ref<DOMRect>(); const autodetectPosition = ( isDropdownDisplayed: Ref<boolean>, togglerElement: HTMLElement | null = null, dropdownElement: HTMLDivElement | null = null, dropdownContent: Ref<unknown> | ComputedRef<unknown> = ref([]), isUpwardPreferred = false, ) => { // ... } return { autodetectPosition, dropdownTop, dropdownBottom, dropdownLeft, dropdownWidth, isDirectedUpwards, togglerRect, dropdownRect, }; };

Açılır liste konumu için dört değişken artı isDirectedUpwards bayrağı ve hepsini güncelleyen bir fonksiyon var. Ayrıca, toggler'ın ve açılır listelerin Rects'leri için iki değişken döndürüyoruz: bu, örneğin, içeriğin ortasına hizalanması gereken araç ipuçları için kullanışlı olabilir.


Hatırlayacağınız üzere kaydırılabilir üst öğenin kaydırılması ve yeniden boyutlandırılmasıyla da ilgilenmemiz gerekiyor, bu yüzden onu bulmak için bir fonksiyon oluşturalım:

 const getFirstScrollableParent = (element: HTMLElement | null): HTMLElement => { const parentElement = element?.parentElement; if (!parentElement) return document.body; const overflowY = window.getComputedStyle(parentElement).overflowY; if (overflowY === 'scroll' || overflowY === 'auto') return parentElement; return getFirstScrollableParent(parentElement); };


Şimdi main fonksiyonunu ekleyelim:

 const autodetectPosition = ( isDropdownDisplayed: Ref<boolean>, togglerElement: HTMLElement | null = null, dropdownElement: HTMLElement | null = null, dropdownContent: Ref<unknown> | ComputedRef<unknown> = ref([]), isUpwardPreferred = false, ) => { if (!togglerElement || !dropdownElement) return; const updateDropdownAttributes = () => { togglerRect.value = togglerElement.getBoundingClientRect(); dropdownRect.value = dropdownElement.getBoundingClientRect(); dropdownWidth.value = `${togglerRect.value.width}px`; dropdownBottom.value = `${window.innerHeight - togglerRect.value.top}px`; dropdownTop.value = `${ window.innerHeight - togglerRect.value.bottom - dropdownRect.value.height }px`; dropdownLeft.value = `${togglerRect.value.left}px`; }; const handleResize = () => { requestAnimationFrame(updateDropdownAttributes); }; const handleScroll = () => { requestAnimationFrame(updateDropdownAttributes); }; watch( [isDropdownDisplayed, dropdownContent], ([newVal, _]) => { const scrollableParent = getFirstScrollableParent(togglerElement); if (!newVal) { window.removeEventListener('resize', handleResize); window.removeEventListener('scroll', handleScroll); scrollableParent.removeEventListener('resize', handleResize); scrollableParent.removeEventListener('scroll', handleScroll); return; } requestAnimationFrame(() => { const distanceFromBottom = window.innerHeight - togglerElement.getBoundingClientRect().bottom; const distanceFromTop = togglerElement.getBoundingClientRect().top; const dropdownHeight = dropdownElement.offsetHeight; isDirectedUpwards.value = isUpwardPreferred ? distanceFromTop > dropdownHeight : distanceFromBottom < dropdownHeight && distanceFromTop > dropdownHeight; updateDropdownAttributes(); window.addEventListener('resize', handleResize); window.addEventListener('scroll', handleScroll); scrollableParent.addEventListener('resize', handleResize); scrollableParent.addEventListener('scroll', handleScroll); }); }, { deep: true }, ); };

isDropdownDisplayed ve dropdownContent geçiriyoruz ki onların güncellemelerine tepki verebilelim.


Ayrıca pozisyonu hesaplamak için ihtiyacımız olan togglerElement ve dropdownElement elemanlarını da gönderiyoruz.


Son olarak, varsayılan olarak toggle'ın üstündeki açılır menüyü istiyorsanız isUpwardPreferred seçeneğini kullanabilirsiniz.

Rahatlama ve Eğlenme Zamanı

Bileşeninizde buna benzer bir şeye ihtiyacınız olacak (şablondaki toggler'ınıza ve açılır menünüze referanslar eklediğinizi varsayıyorum):

 const { autodetectPosition, dropdownTop, dropdownBottom, dropdownLeft, dropdownWidth, isDirectedUpwards, } = useDropdownAttributes(); const togglerRef = ref<HTMLElement>(); const dropdownRef = ref<HTMLElement>(); const isDropdownShown = ref(false); onMounted(() => { autodetectPosition(isDropdownShown, togglerRef.value?.$el, dropdownRef.value?.$el); });


Ve CSS şu şekilde görünecek:

 .dropdown { position: fixed; bottom: v-bind('isDirectedUpwards ? dropdownBottom : dropdownTop'); left: v-bind('dropdownLeft'); width: v-bind('dropdownWidth'); min-width: 0; }


İşte oldu. Açılır liste taştığında bile düzgün bir şekilde görüntüleniyor ve altında yeterli alan yoksa geçiş düğmesinin üstüne taşınıyor.



Ve makalenin sonuna geldiğimiz için, size neşeli bir şey bırakmak isterdim—ama fikirlerim tükendi. Bu yüzden, korkarım bu sefer elimde sadece "iyi şanslar" var. İyi şanslar. 👋


Kodun tamamını GitHub'da bulabilirsiniz.

Trending Topics

blockchaincryptocurrencyhackernoon-top-storyprogrammingsoftware-developmenttechnologystartuphackernoon-booksBitcoinbooks