випуск замовлення onclick () та onblur ()


102

У мене є поле введення, яке відображає користувацьке спадне меню. Я хотів би наступної функціональності:

  • Коли користувач натискає будь-яке місце поза полем введення, меню слід видалити.
  • Якщо, більш конкретно, користувач натискає на div всередині меню, меню слід видалити, і повинна відбутися спеціальна обробка, на основі якої було натиснуто div.

Ось моя реалізація:

У полі введення є onblur()подія, яка видаляє меню (шляхом встановлення батьківського innerHTMLполя на порожній рядок) кожного разу, коли користувач клацне за межами поля введення. У дівах всередині меню також є onclick()події, які виконують спеціальну обробку.

Проблема полягає в тому, що onclick()події ніколи не спрацьовують при натисканні на меню, оскільки onblur()спочатку вводиться поле введення та видаляє меню, включаючи onclick()s!

Я вирішив цю проблему, розділивши меню divs ' onclick()на onmousedown()і onmouseup()події та встановивши глобальний прапор на миші вниз, який очищається на миші вгору, подібно до того, що було запропоновано у цій відповіді . Оскільки onmousedown()запускається раніше onblur(), прапор буде встановлено, onblur()якщо було натиснено один із меню, який знаходиться в меню, але не якщо це було десь на екрані. Якщо меню було натиснене, я негайно повертаюсь з нього, onblur()не вилучаючи меню, а потім чекаю, поки onclick()запуститься, і тоді я можу сміливо видалити меню.

Чи є більш елегантне рішення?

Код виглядає приблизно так:

<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;
    ...
}

Відповіді:


168

У мене виникли такі самі проблеми, як і у вас, мій інтерфейс призначений саме так, як ви описуєте. Я вирішив проблему, просто замінивши onClickелементи меню на an onMouseDown. Я більше нічого не робив; ні onMouseUp, прапорів немає. Це вирішило проблему, дозволивши браузеру автоматично повторно замовляти, виходячи з пріоритетності цих обробників подій, без додаткових робіт від мене.

Чи є якась причина, чому це не працювало б і для вас?


3
Це насправді працює. Я перевірив його на FF, і він працює як шарм.
Верховний Дельфін

3
Це працює. Схоже onmousedown, виконується перед onblurтестуванням у Firefox, Chrome та Internet Explorer.
Тонатіо

11
Варто зазначити, що це трохи змінює поведінку - тоді взаємодія при натисканні обробляється мишею вниз, а не мишкою вгору. Для більшості людей це може бути добре (самовключення в цьому випадку), але є кілька недоліків. Найбільш помітно, що я часто натискаю та перетягую кнопку, якщо я неправильно натиснув, що запобігає виклику onclick - якщо кнопка виконує нечисту функцію (видалення, повідомлення тощо), можливо, ви захочете зберегти це і піти з підходом до прапора.
Брізе

2
Як щодо доступності в той момент, коли ви встановили mouseDown замість onClick, ви
знищуєте

2
Відповідь, перелічена нижче @ ian-macfarlane, - це правильний спосіб вирішення цього питання. У зведенні ... onMouseDownз event.preventDefault() і onClickз потрібним дією.
Conal Trinitrotoluene Da Costa

47

onClickне слід замінювати onMouseDown.

Хоча цей підхід дещо працює , ці два принципово різні події, які мають різні очікування в очах користувача. Використання onMouseDownзамість onClickцього зруйнує передбачуваність вашого програмного забезпечення в цьому випадку. Таким чином, дві події є незмінними.

Для ілюстрації: при випадковому натисканні на кнопку користувачі очікують, що зможуть утримувати клацання миші, перетягнути курсор за межі елемента та відпустити кнопку миші, зрештою, в результаті не буде дії. onClickробить це. onMouseDownне дозволяє користувачеві утримувати мишу, а натомість негайно спровокує дію без будь-якого звернення до користувача. onClick- це стандарт, за яким ми очікуємо запускати дії на комп’ютері.

У цій ситуації зателефонуйте event.preventDefault()на onMouseDownподію. onMouseDownвикличе подія замикання за замовчуванням, і не зробить цього при preventDefaultвиклику. Тоді, onClickбуде шанс викликатися. Розмиття події все-таки відбудеться лише після onClick.

Зрештою, onClickподія є поєднанням onMouseDownі onMouseUp, якщо і лише тоді, коли вони обидва відбуваються в межах одного елемента.


7
Це було ідеальним рішенням для мене, і коментар цілком коректний - заміна на onMouseDown є заплутаною для користувачів. Голосували.
Пол Бартлетт

5
це має бути правильна відповідь. дякую за повідомлення про це
користувач1189352

1
Варто зазначити, що це має і деякі незначні побічні ефекти, наприклад, неможливість вибору тексту натисканням всередині елемента. Не можу придумати надто багато випадків, коли це було б проблемою, просто це виглядає трохи смішно.
Рей Міясака

1
Якщо я використовую event.preventDefault()на onMouseDownподія, моє onFocusподія не називається
Batman

4

Замінити на onmousedownз onfocus. Тож ця подія буде ініційована, коли фокус знаходиться всередині текстового поля.

Замінити на onmouseupз onblur. Щойно виймете фокус із текстового поля, onblur виконається.

Я думаю, це те, що вам може знадобитися.

ОНОВЛЕННЯ :

коли ви виконуєте свою функцію onfocus -> видаліть класи, які ви будете застосовувати в onblur, і додайте класи, які ви хочете виконати onfocus

і

коли ви виконуєте свою функцію onblur -> видаліть класи, які ви будете застосовувати в onfocus, і додайте класи, які ви хочете виконати onblur

Я не бачу необхідності змінних прапорців.

ОНОВЛЕННЯ 2:

Ви можете використовувати події onmouseout та onmouseover

onmouseover- Визначає, коли курсор над ним.

onmouseout- Визначає, коли курсор залишає.


Я відредагував своє питання для наочності. Як це рішення дозволяє уникнути необхідності встановлення прапора? Також я використовую onmousedown()і onmouseup()меню, а не поле для введення тексту / введення.
1 ''

Я ще не можу прийняти цю відповідь, бо не знаю, що вона правильна. Чи можете ви відповісти на занепокоєння, яке я висловив у своєму коментарі вище?
1 ''

Звичайно, я бачу, як ви могли використовувати onmouseover і onmouseout аналогічно тому, що я зараз роблю, щоб встановити прапор, коли миша над меню. Однак я все-таки думаю, що вам потрібно буде встановити прапор у режимі onmouseover, який очищається в onmouseout, щоб ви могли знати, чи перебуваєте ви над меню при натисканні. Чи можете ви придумати спосіб, який не потребує встановлення прапора?
1 ''

Чи можу я побачити код ... помістіть його у загадку ... просто поставте відповідну частину та її повністю нормально, якщо я не бачу результатів ..
просто вставте


2

Більш елегантне (але, ймовірно, менш ефективне) рішення:

Замість того, щоб використовувати onblur введення для видалення меню, використовуйте document.onclick, що запускається після onblur.

Однак це також означає, що меню видаляється при натисканні на сам вхід, що є небажаною поведінкою. Встановити input.onclickз , event.stopPropagation()щоб уникнути розмноження клацання в разі документа клацання.


5
Що ж, проблема з цією відповіддю полягає в тому, що якщо це не подія клацання, яка в результаті викликала розмиття. Що робити, якщо користувач вклав із входу? Це призведе до запуску події розмиття, але меню розгортання все ще буде видно.
Крістоф

0

змінити onclick від onfocus

навіть якщо onblur і onclick не дуже добре уживаються, але, очевидно, онфокус і так onblur. оскільки навіть після закриття меню фокус все ще діє для елемента, натиснутого всередині.

Я так і працював.


-7

Ви можете використовувати setIntervalфункцію всередині вашого onBlurобробника, наприклад:

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

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

setIntervalфункція видалити onBlur функцію зі стека викликів, додати , тому що ви встановите час на 0, ця функція буде викликана відразу ж після того, як інший обробник події закінчив


5
setIntervalце погана ідея, ви ніколи не скасовуєте її, тому вона буде постійно виконувати вашу функцію і, швидше за все, спричиняє ваш сайт використовувати макс. процесор. Використовуйте setTimeoutнатомість, якщо ви збираєтесь використовувати цю техніку (що я не рекомендую). Зробити додаток залежним від часу проведення певних подій - це ризикована справа.
кейсі

6
НЕБЕЗПЕЧНО БУДЕ РОБІНСОН! НЕБЕЗПЕЧНО! Стан гонки попереду!
GoreDefex

Я спочатку придумав це рішення (добре, що setTimeoutне setInterval), але мені довелося зіткнути його до 250мс, перш ніж терміни відбудуться у правильному порядку. Ця помилка, ймовірно, все ще буде існувати на повільних пристроях, а також робить затримку помітною. Я спробував, onMouseDownі це працює чудово. Цікаво, чи потрібні також події на дотик.
Бренан Чеун

Щось на зразок інтервалу непогано, якщо ви дійсно хочете використовувати onClick. Але ви хочете, щоб рекурсивний час очікувався з умовою, а не інтервалом, щоб він не працював вічно. Це та сама картина, яка використовується умовними чеками Селену.
Буде Каїн
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.