vấn đề đặt hàng onclick () và onblur ()


102

Tôi có một trường nhập liệu hiển thị menu thả xuống tùy chỉnh. Tôi muốn các chức năng sau:

  • Khi người dùng nhấp vào bất kỳ đâu bên ngoài trường nhập, menu sẽ được xóa.
  • Cụ thể hơn, nếu người dùng nhấp vào một div bên trong menu, menu sẽ bị xóa và quá trình xử lý đặc biệt sẽ xảy ra dựa trên div đã được nhấp.

Đây là cách triển khai của tôi:

Trường đầu vào có một onblur()sự kiện xóa menu (bằng cách đặt giá trị gốc của nó innerHTMLthành một chuỗi trống) bất cứ khi nào người dùng nhấp vào bên ngoài trường nhập. Các div bên trong menu cũng có onclick()các sự kiện thực hiện xử lý đặc biệt.

Vấn đề là các onclick()sự kiện không bao giờ kích hoạt khi nhấp vào menu, vì trường đầu vào onblur()kích hoạt trước và xóa menu, bao gồm cả onclick()s!

Tôi đã giải quyết vấn đề bằng cách tách menu divs ' onclick()thành onmousedown()onmouseup()các sự kiện và đặt cờ toàn cục khi di chuột xuống, cờ này được xóa khi di chuột lên, tương tự như những gì được đề xuất trong câu trả lời này . Bởi vì onmousedown()kích hoạt trước đó onblur(), cờ sẽ được đặt onblur()nếu một trong các div trình đơn được nhấp vào, nhưng không phải nếu ở một nơi khác trên màn hình. Nếu menu được nhấp vào, tôi ngay lập tức quay lại từ đó onblur()mà không xóa menu, sau đó chờ onclick()kích hoạt, lúc đó tôi có thể xóa menu một cách an toàn.

Có một giải pháp thanh lịch hơn?

Mã trông giống như sau:

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

Câu trả lời:


167

Tôi cũng gặp phải vấn đề giống như bạn, giao diện người dùng của tôi được thiết kế chính xác như bạn mô tả. Tôi đã giải quyết vấn đề bằng cách thay thế đơn giản onClickcho các mục menu bằng một onMouseDown. Tôi không làm gì khác; không onMouseUp, không có cờ. Điều này đã giải quyết vấn đề bằng cách cho phép trình duyệt tự động sắp xếp lại dựa trên mức độ ưu tiên của các trình xử lý sự kiện này mà không cần tôi thực hiện thêm bất kỳ công việc nào.

Có lý do gì khiến điều này không phù hợp với bạn không?


3
Điều này thực sự hoạt động. Tôi đã thử nghiệm nó trong FF và nó hoạt động như một sự quyến rũ.
Cá heo tối cao

3
Nó hoạt động. Có vẻ như onmousedownnó được thực thi trước khi onblurKiểm tra trong Firefox, Chrome và Internet Explorer.
Tonatio,

11
Cần lưu ý rằng điều này sẽ thay đổi hành vi một chút tự nhiên - tương tác nhấp chuột sau đó được xử lý khi di chuột xuống chứ không phải di chuột lên. Đối với hầu hết mọi người, điều đó có thể ổn (tự bao gồm trong trường hợp này) nhưng có một vài nhược điểm. Đáng chú ý nhất là tôi thường nhấp sau đó kéo nút ra khỏi nút nếu tôi bấm nhầm, điều này ngăn chặn việc gọi onclick - nếu nút thực hiện chức năng không phải Pure (xóa, đăng, v.v.), bạn có thể muốn giữ nguyên điều này và đi với phương pháp cờ.
Brizee

2
Còn về khả năng tiếp cận tại thời điểm bạn đặt mouseDown thay vì onClick bạn giết khả năng tiếp cận cho tất cả <button> yếu tố: /
ncubica

2
Câu trả lời được liệt kê dưới đây bởi @ ian-macfarlane là cách chính xác để giải quyết vấn đề này. Trong Tóm tắt ... onMouseDownvới event.preventDefault()onClickvới hành động mong muốn của bạn.
Conal Trinitrotoluene Da Costa

46

onClickkhông nên được thay thế bằng onMouseDown.

Mặc dù cách tiếp cận này có phần hiệu quả , nhưng về cơ bản cả hai đều là những sự kiện khác nhau, có những kỳ vọng khác nhau trong mắt người dùng. Sử dụng onMouseDownthay vì onClicksẽ làm hỏng khả năng dự đoán của phần mềm của bạn trong trường hợp này. Do đó, hai sự kiện không thể hoán đổi cho nhau.

Để minh họa: khi vô tình nhấp vào một nút, người dùng mong đợi có thể giữ nhấp chuột, kéo con trỏ bên ngoài phần tử và thả nút chuột, cuối cùng dẫn đến không có hành động. onClickthực hiện điều này. onMouseDownkhông cho phép người dùng giữ chuột và thay vào đó sẽ ngay lập tức kích hoạt một hành động mà không cần người dùng nào. onClicklà tiêu chuẩn mà chúng tôi mong đợi để kích hoạt các hành động trên máy tính.

Trong tình huống này, hãy gọi event.preventDefault()trên onMouseDownsự kiện. onMouseDownsẽ gây ra sự kiện mờ theo mặc định và sẽ không làm như vậy khi preventDefaultđược gọi. Khi đó, onClicksẽ có cơ hội được gọi. Một sự kiện mờ vẫn sẽ xảy ra, chỉ sau đó onClick.

Xét cho cùng, onClicksự kiện là sự kết hợp của onMouseDownonMouseUp, nếu và chỉ khi cả hai đều xảy ra trong cùng một phần tử.


7
Đây là giải pháp hoàn hảo cho tôi và nhận xét là hoàn toàn chính xác - việc thay thế onMouseDown gây nhầm lẫn cho trải nghiệm người dùng. Đã bỏ phiếu.
Paul Bartlett

5
đây phải là câu trả lời chính xác. cảm ơn bạn đã đăng bài này
user1189352

1
Cần lưu ý rằng điều này cũng có một số tác dụng phụ nhỏ, chẳng hạn như không thể chọn văn bản bằng cách nhấp vào bên trong phần tử. Không thể nghĩ ra quá nhiều trường hợp mà đây sẽ là một vấn đề, chỉ là nó cảm thấy hơi buồn cười.
Rei Miyasaka

1
Nếu tôi sử dụng event.preventDefault()trong onMouseDownsự kiện, sự kiện của tôi onFocussẽ không được gọi
Batman

4

Thay thế onmousedownbằngonfocus . Vì vậy, sự kiện này sẽ được kích hoạt khi tiêu điểm nằm bên trong hộp văn bản.

Thay thế onmouseupbằngonblur . Thời điểm bạn lấy tiêu điểm ra khỏi hộp văn bản, onblur sẽ thực thi.

Tôi đoán đây là những gì bạn có thể cần.

CẬP NHẬT :

khi bạn thực thi hàm onfocus của mình -> xóa các lớp mà bạn sẽ áp dụng trong onblur và thêm các lớp mà bạn muốn được thực thi onfocus

khi bạn thực thi hàm onblur -> xóa các lớp mà bạn sẽ áp dụng trong onfocus và thêm các lớp mà bạn muốn thực thi onblur

Tôi không thấy bất kỳ nhu cầu nào của các biến cờ.

CẬP NHẬT 2:

Bạn có thể sử dụng các sự kiện onmouseout và onmouseover

onmouseover -Phát hiện khi con trỏ ở trên nó.

onmouseout -Phát hiện khi nào con trỏ rời đi.


Tôi đã chỉnh sửa câu hỏi của mình cho rõ ràng. Làm thế nào để giải pháp này tránh được sự cần thiết phải đặt cờ? Ngoài ra, tôi sử dụng onmousedown()onmouseup()trên menu, không phải trường văn bản / đầu vào.
1 ''

Tôi chưa thể chấp nhận câu trả lời này vì tôi không biết rằng nó chính xác. Bạn có thể trả lời những mối quan tâm mà tôi đã nêu ra trong nhận xét của tôi ở trên không?
1 ''

Chắc chắn, tôi có thể thấy cách bạn có thể sử dụng onmouseover và onmouseout tương tự như những gì tôi hiện đang làm, để đặt cờ khi chuột ở trên menu. Tuy nhiên, tôi vẫn nghĩ rằng bạn sẽ cần phải đặt cờ trong onmouseover được xóa trong onmouseout, để bạn có thể biết liệu bạn có ở trên menu khi nhấp vào hay không. Bạn có thể nghĩ ra một cách không cần đặt cờ không?
1 ''

Tôi có thể xem mã của bạn không ... hãy đặt nó vào một cách khó hiểu ... chỉ cần đặt phần liên quan và nó hoàn toàn ổn nếu tôi không thấy kết quả..chỉ cần mã ..
HIRA Thakur


2

Một giải pháp thanh lịch hơn (nhưng có thể kém hiệu quả hơn):

Thay vì sử dụng onblur của đầu vào để xóa menu, hãy sử dụng document.onclick, kích hoạt sauonblur .

Tuy nhiên, điều này cũng có nghĩa là menu bị xóa khi chính đầu vào được nhấp vào, đây là hành vi không mong muốn. Đặt input.onclickvới event.stopPropagation()để tránh các lần nhấp lan truyền đến sự kiện nhấp vào tài liệu.


5
Tuy nhiên, vấn đề với câu trả lời này là nếu nó không phải là một sự kiện nhấp chuột kết thúc gây ra hiện tượng mờ. Điều gì sẽ xảy ra nếu người dùng tab ra khỏi đầu vào? Điều đó sẽ khiến sự kiện mờ kích hoạt nhưng menu flyout vẫn hiển thị.
Christoph

0

thay đổi onclick bằng onfocus

ngay cả khi onblur và onclick không ăn ý lắm, nhưng rõ ràng là onfocus và có onblur. vì ngay cả sau khi đóng menu, onfocus vẫn có giá trị cho phần tử được nhấp vào bên trong.

Tôi đã làm và nó đã có hiệu quả.


-7

Bạn có thể sử dụng một setIntervalhàm bên trong onBlurtrình xử lý của mình , như sau:

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

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

các setIntervalchức năng sẽ loại bỏ của bạn onBlur chức năng ra từ các cuộc gọi stack, thêm vì bạn thiết lập thời gian để 0, chức năng này sẽ được gọi ngay sau khi xử lý sự kiện khác đã hoàn thành


5
setIntervallà một ý tưởng tồi, bạn không bao giờ hủy nó, vì vậy nó sẽ liên tục chạy chức năng của bạn và rất có thể khiến trang web của bạn sử dụng cpu tối đa. Sử dụng setTimeoutthay thế nếu bạn định sử dụng kỹ thuật này (mà tôi không khuyến khích). Làm cho ứng dụng của bạn phụ thuộc vào thời gian của các sự kiện nhất định là một công việc kinh doanh đầy rủi ro.
casey

6
NGUY HIỂM SẼ ROBINSON! NGUY HIỂM! Tình trạng cuộc đua phía trước!
GoreDefex

Ban đầu tôi đã nghĩ ra giải pháp này (cũng setTimeoutkhông phải setInterval) nhưng tôi phải tăng nó lên 250ms trước khi thời gian diễn ra theo đúng thứ tự. Lỗi này có thể sẽ vẫn tồn tại trên các thiết bị chậm hơn và nó cũng làm cho độ trễ đáng chú ý. Tôi đã thử onMouseDownvà nó hoạt động tốt. Tôi tự hỏi nếu nó cũng cần các sự kiện cảm ứng.
Brennan Cheung

Một cái gì đó giống như một khoảng thời gian không phải là xấu nếu bạn thực sự muốn sử dụng onClick. Nhưng bạn muốn thời gian chờ đệ quy với một điều kiện hơn là một khoảng thời gian để nó không chạy mãi mãi. Đây là mẫu tương tự được sử dụng bởi các đợi có điều kiện Selenium.
Will Cain
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.