onclick () ve onblur () sipariş sorunu


107

Özel bir açılır menüyü getiren bir giriş alanım var. Aşağıdaki işlevselliği istiyorum:

  • Kullanıcı, giriş alanının dışında herhangi bir yere tıkladığında, menü kaldırılmalıdır.
  • Daha spesifik olarak, kullanıcı menü içindeki bir div öğesini tıklarsa, menü kaldırılmalı ve hangi div öğesinin tıklandığına bağlı olarak özel işlemler gerçekleştirilmelidir.

İşte benim uygulamam:

Giriş alanında, kullanıcı giriş alanının dışına her tıkladığında onblur()menüyü silen (üst öğelerini innerHTMLboş bir dizeye ayarlayarak) bir olay vardır . Menü içindeki div'ler ayrıca onclick()özel işlemeyi yürüten olaylara da sahiptir .

Sorun şu ki, onclick()menü tıklandığında olayların asla tetiklenmemesi, çünkü onblur()ilk önce giriş alanı tetikleniyor ve onclick()s!

Sorunu, menü onclick()bölmelerini onmousedown()ve onmouseup()olaylarını bölerek ve bu yanıtta önerildiği gibi, fare aşağıdayken farenin yukarısında temizlenen global bir bayrak belirleyerek çözdüm . Daha onmousedown()önce patladığı için, menü div'lerinden biri tıklandığında onblur()bayrak ayarlanacaktır onblur(), ancak ekranda başka bir yer varsa değil. Menü tıklandıysa menüyü onblur()silmeden hemen geri dönüyorum, ardından onclick()ateşlenmesini bekliyorum, bu noktada menüyü güvenle silebilirim.

Daha zarif bir çözüm var mı?

Kod şuna benzer:

<div class="menu" onmousedown="setFlag()" onmouseup="doProcessing()">...</div>
<input id="input" onblur="removeMenu()" ... />

var mouseflag;

function setFlag() {
    mouseflag = true;
}

function removeMenu() {
    if (!mouseflag) {
        document.getElementById('menu').innerHTML = '';
    }
}

function doProcessing(id, name) {
    mouseflag = false;
    ...
}

Yanıtlar:


177

Ben de sizinle aynı sorunu yaşıyordum, kullanıcı arayüzüm tam olarak sizin tanımladığınız gibi tasarlandı. Sorunu basitçe onClickfor menü öğelerini bir ile değiştirerek çözdüm onMouseDown. Ben başka hiçbir şey yapmadım; hayır onMouseUp, bayrak yok. Bu, tarayıcının benden herhangi bir ek çalışma yapmadan bu olay işleyicilerin önceliğine göre otomatik olarak yeniden sipariş vermesine izin vererek sorunu çözdü.

Bunun sizin için de işe yaramaması için herhangi bir sebep var mı?


3
Bu aslında işe yarıyor. FF'de test ettim ve cazibe gibi çalışıyor.
Yüksek Dolphin

3
İşe yarıyor. Firefox, Chrome ve Internet Explorer'da Test onmousedownedilmeden önce yürütülmüş gibi görünüyor onblur.
Tonatio

12
Bunun davranışı biraz doğal bir şekilde değiştirdiğini belirtmek gerekir - daha sonra tıklama etkileşimi, farenin yukarı kaldırılması yerine fare aşağıdayken işlenir. Pek çok insan için iyi olabilir (bu durumda kendisi dahil), ancak birkaç dezavantajı vardır. En önemlisi, genellikle yanlış tıkladıysam düğmeyi tıklayıp sonra sürüklerim, bu da onclick'in çağrılmasını engeller - düğme Pure olmayan bir işlev gerçekleştirirse (silme, gönderme vb.), Bunu korumak ve bayrak yaklaşımını kullanmak isteyebilirsiniz.
Brizee

2
Erişilebilirlik onClick yerine mouseDown'u ayarladığınız andaki tüm <button> öğeleri için erişilebilirliği
sonlandırırsınız

2
Aşağıda @ ian-macfarlane tarafından listelenen yanıt, bu sorunu ele almanın doğru yoludur. Özet olarak ... onMouseDownile event.preventDefault() ve onClickistediğiniz eylem ile.
Conal Trinitrotoluene Da Costa

56

onClickile değiştirilmemelidir onMouseDown.

Bu yaklaşım bir şekilde işe yarasa da , ikisi temelde farklı olaylardır ve kullanıcının gözünde farklı beklentiler vardır. onMouseDownBunun yerine kullanmak onClick, bu durumda yazılımınızın öngörülebilirliğini bozacaktır. Bu nedenle, iki olay birbirinin yerine geçemez.

Örneğin, kullanıcılar yanlışlıkla bir düğmeye tıkladıklarında, fare tıklamasını basılı tutabilmeyi, imleci öğenin dışına sürükleyebilmeyi ve fare düğmesini bırakarak sonuçta hiçbir işlem yapılmamasını bekler. onClickbunu yapar. onMouseDownkullanıcının fareyi basılı tutmasına izin vermez ve bunun yerine kullanıcıya herhangi bir rücu olmaksızın hemen bir eylemi tetikler. onClickbilgisayardaki eylemleri tetiklemesini beklediğimiz standarttır.

Bu durumda, çağrı event.preventDefault()üzerine onMouseDownolay. onMouseDownvarsayılan olarak bir bulanıklığa neden olur ve preventDefaultçağrıldığında bunu yapmaz . Sonra onClickaranma şansı olacak. Ancak bundan sonra bir bulanıklık olayı yine de olacaktır onClick.

Sonuçta, onClickolay bir kombinasyonudur onMouseDownve onMouseUpher ikisi de aynı elementin içinde meydana ancak ve ancak,.


7
Bu benim için mükemmel bir çözümdü ve yorum tamamen doğru - onMouseDown'u değiştirmek kullanıcı deneyimi için kafa karıştırıcı. Oy verin.
Paul Bartlett

5
bu doğru cevap olmalı. bunu gönderdiğiniz için teşekkür ederiz
user1189352

1
Bunun bazı küçük yan etkileri olduğunu da belirtmek gerekir, örneğin öğenin içine tıklayarak metni seçememe. Bunun bir sorun olacağı çok fazla vaka düşünemiyorum, sadece biraz komik hissettiriyor.
Rei Miyasaka

2
Etkinlikte kullanırsam event.preventDefault(), onMouseDownetkinliğim onFocusçağrılmaz
Batman

3

Değiştir onmousedownile onfocus. Bu nedenle, odak metin kutusunun içindeyken bu olay tetiklenecektir.

Değiştir onmouseupile onblur. Odağınızı metin kutusundan çıkardığınız an, onblur çalışacaktır.

Sanırım ihtiyacın olan şey bu.

GÜNCELLEME :

onfocus fonksiyonunuzu çalıştırdığınızda -> onblur'da uygulayacağınız sınıfları kaldırın ve onfocus üzerinde çalıştırılmasını istediğiniz sınıfları ekleyin

ve

işlevinizi onblur üzerinde çalıştırdığınızda -> onfocus'ta uygulayacağınız sınıfları kaldırın ve üzerinde çalıştırılmak istediğiniz sınıfları ekleyin

Bayrak değişkenlerine ihtiyaç görmüyorum.

GÜNCELLEME 2:

Olayları mouseout ve onmouseover üzerinde kullanabilirsiniz.

onmouseover -İmlecin üzerinde olduğunu algılar.

onmouseout - İmlecin ne zaman ayrıldığını algılar.


Soruyu anlaşılır olması için düzenledim. Bu çözüm bir bayrak belirleme ihtiyacını nasıl ortadan kaldırır? Ayrıca, metin kutusu / giriş alanını değil, menüde onmousedown()ve kullanıyorum onmouseup().
1

Bu yanıtı henüz kabul edemiyorum çünkü doğru olup olmadığını bilmiyorum. Yukarıdaki yorumumda dile getirdiğim endişelere cevap verebilir misiniz?
1 ''

Elbette, fare menünün üzerindeyken bir bayrak ayarlamak için şu anda yaptığıma benzer şekilde onmouseover ve onmouseout'u nasıl kullanabileceğinizi görebiliyorum. Yine de, tıkladığınızda menünün üzerinde olup olmadığınızı bilmeniz için onmouseover'da onmouseout'ta temizlenen bir bayrak ayarlamanız gerektiğini düşünüyorum. Bayrak koymayı gerektirmeyen bir yol düşünebiliyor musunuz?
1 ''

Kodunuzu görebilir miyim ... bir kemanla koyun ... sadece ilgili kısmı koyun ve sonuçları görmezsem tamamen tamam .. sadece kodu ..
HIRA THAKUR


2

Daha zarif (ancak muhtemelen daha az performanslı) bir çözüm:

Bunun yerine menü kullanımını kaldırmak için girişin onblur kullanmanın document.onclicksonra patlar, onblur.

Bununla birlikte, bu aynı zamanda, istenmeyen bir davranış olan giriş tıklandığında menünün kaldırıldığı anlamına gelir. Tıklamaların belge tıklama olayına yayılmasını önlemek için bir input.onclickile ayarlayın event.stopPropagation().


5
Bu yanıtla ilgili sorun, bunun bulanıklığı tetikleyen bir tıklama olayı olmamasıdır. Ya kullanıcı girişten sekme çıkarsa? Bu, bulanıklaştırma olayının tetiklenmesine neden olur, ancak açılır menü yine de görünür olacaktır.
Christoph

0

onfocus ile onclick'i değiştirin

onblur ve onclick çok iyi anlaşmasa bile, açıkçası odak noktası ve evet bulanıklık. çünkü menü kapatıldıktan sonra bile odak, içeriye tıklanan öğe için hala geçerlidir.

Yaptım ve işe yaradı.


-7

İşleyicinizin setIntervaliçinde aşağıdaki onBlurgibi bir işlev kullanabilirsiniz :

<input id="input" onblur="removeMenu()" ... />

function removeMenu() {
    setInterval(function(){
        if (!mouseflag) {
            document.getElementById('menu').innerHTML = '';
        }
    }, 0);
}

setIntervalişlevi kaldıracaktır onBlur 0'a çünkü sen eklemek, çağrı yığınından ayarlanan zaman işlevini, bu işlev başka olay işleyicisi bitmiş hemen sonra çağrılır


5
setIntervalkötü bir fikir, onu asla iptal etmiyorsunuz, bu nedenle sürekli olarak işlevinizi çalıştıracak ve büyük olasılıkla sitenizin max cpu kullanmasına neden olacaktır. setTimeoutBu tekniği kullanacaksanız bunun yerine kullanın (tavsiye etmiyorum). Uygulamanızı belirli olayların zamanlamasına bağlı hale getirmek riskli bir iştir.
casey

6
TEHLİKE ROBINSON OLACAK! TEHLİKE! Önde yarış durumu!
GoreDefex

Başlangıçta bu çözümü buldum (iyi setTimeoutdeğil setInterval), ancak 250zamanlama doğru sırada gerçekleşmeden önce onu ms'ye yükseltmek zorunda kaldım . Bu hata muhtemelen daha yavaş cihazlarda hala var olacak ve ayrıca gecikmeyi farkedilir kılıyor. Denedim onMouseDownve iyi çalışıyor. Dokunma olaylarına da ihtiyacı var mı merak ediyorum.
Brennan Cheung

OnClick'i gerçekten kullanmak istiyorsanız, aralık gibi bir şey fena değildir. Ancak, sonsuza kadar çalışmaması için bir aralık yerine bir koşul içeren özyinelemeli bir zaman aşımı istersiniz. Bu, Selenium koşullu beklemelerde kullanılan kalıpla aynıdır.
Will Cain
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.