2012-08-30 17 views
10

Cho hai phần tử HTML A và B trong cùng một tài liệu, làm cách nào tôi có thể tìm ra phần tử nào "gần hơn" với người dùng (tức là nếu chúng chồng lên nhau, cái nào che khuất)?So sánh các phần tử HTML theo z-index thực tế

Các W3C CSS Specification mô tả ngữ cảnh xếp chồng, mà công cụ hiển thị tuân thủ sẽ thực hiện. Tuy nhiên, tôi không thể tìm cách truy cập thông tin này trong chương trình JavaScript, trình duyệt chéo hay không. Tất cả những gì tôi có thể đọc là thuộc tính css z-index, mà mọi người không nói nhiều, vì phần lớn thời gian được đặt thành auto hoặc, ngay cả khi được biểu thị dưới dạng giá trị số, không phải là chỉ báo đáng tin cậy về cách hiển thị thực sự (nếu chúng thuộc về các bối cảnh thống kê khác nhau, so sánh chỉ mục z là không liên quan).

Xin lưu ý rằng tôi quan tâm đến các yếu tố tùy ý: nếu cả hai yếu tố bên dưới con trỏ chuột, chỉ một phần tử được coi là "di chuột", vì vậy tôi có thể dễ dàng tìm thấy con gần nhất trong trường hợp này. Tuy nhiên, tôi đang tìm một giải pháp tổng quát hơn, tốt nhất là giải pháp không liên quan đến việc triển khai lại thuật toán xếp chồng mà công cụ hiển thị đã thực hiện.

Cập nhật: hãy để tôi làm rõ một chút lý do đằng sau câu hỏi này: Gần đây tôi đã giải quyết a question rằng tiếp xúc với một hạn chế trong cơ chế kéo và thả jQuery - nó không mất z-chỉ số vào tài khoản khi thả, vì vậy nếu một phần tử đang che khuất một phần tử khác, nó vẫn có thể thực hiện thao tác thả trong phần tử "đằng sau". Trong khi câu hỏi được liên kết được trả lời cho trường hợp đặc biệt OP, vấn đề chung vẫn tồn tại và không có giải pháp dễ dàng nào mà tôi biết.

alex's answer dưới đây là hữu ích, nhưng không đủ đối với trường hợp trong tầm tay: khi kéo, yếu tố kéo bản thân (hay chính xác hơn helper của nó) là yếu tố trên cùng dưới con trỏ chuột, vì vậy elementFromPoint sẽ trở lại thay vì tiếp theo yếu tố trên cùng mà chúng tôi thực sự cần (giải pháp thay thế:style the cursor do đó nó được đặt bên ngoài trình trợ giúp). Các chiến lược giao nhau khác mà jQuery sử dụng cũng đưa vào tài khoản nhiều hơn một điểm, làm phức tạp nhiệm vụ xác định phần tử trên cùng giao cắt người trợ giúp bằng cách nào đó. Việc có thể so sánh (hoặc sắp xếp) các yếu tố theo chỉ số z thực tế sẽ làm cho chế độ giao lộ "z-index aware" khả thi cho trường hợp chung.

Trả lời

1

Sau một vài ngày nghiên cứu, tôi nghĩ rằng tôi đã triển khai lại thành công cơ chế xếp chồng theo các quy tắc của năm 2016. Về cơ bản tôi đã cập nhật phương pháp 2013 (được đăng bởi OP). Kết quả là một hàm so sánh hai nút DOM và trả về một nút trực quan.

front = $.fn.visuallyInFront(document.documentElement, document.body); 
// front == <body>...</body> because the BODY node is 'on top' of the HTML node 

Có nhiều cách khác để xác định yếu tố nào là trên đầu trang của người kia. Ví dụ: document.elementFromPoint() hoặc document.elementsFromPoint() mùa xuân cần lưu ý. Tuy nhiên, có rất nhiều (không có giấy tờ) yếu tố ảnh hưởng đến độ tin cậy của các phương pháp này. Ví dụ: độ mờ, khả năng hiển thị, sự kiện con trỏ, khả năng hiển thị ngược và một số biến đổi có thể khiến document.elementFromPoint() không thể thử nghiệm thành phần cụ thể. Và sau đó có vấn đề là document.elementFromPoint() chỉ có thể truy vấn phần tử cao nhất (không phải là phần tử cơ bản). Điều này cần được giải quyết với document.elementsFromPoint(), nhưng hiện tại chỉ được triển khai trong Chrome. Ngoài ra, tôi đã gửi một lỗi với các nhà phát triển Chrome về số document.elementsFromPoint(). Khi nhấn thử một thẻ neo, tất cả các phần tử bên dưới sẽ không được chú ý.

Tất cả các vấn đề này kết hợp khiến tôi quyết định thử thực hiện lại cơ chế xếp chồng. Lợi ích của phương pháp này là cơ chế xếp chồng được ghi chép khá rộng rãi và có thể được kiểm tra và hiểu rõ.

Làm thế nào nó hoạt động

tôi cách tiếp cận tái thực hiện các cơ chế HTML xếp chồng. Nó nhằm mục đích tuân thủ chính xác tất cả các quy tắc ảnh hưởng đến thứ tự xếp chồng của các phần tử HTML. Điều này bao gồm các quy tắc định vị, nổi, thứ tự DOM nhưng cũng có các thuộc tính CSS3 như độ mờ đục, biến đổi và các thuộc tính thử nghiệm khác như bộ lọc và mặt nạ. Các quy tắc dường như được triển khai chính xác kể từ tháng 3 năm 2016, nhưng sẽ cần phải được cập nhật trong tương lai khi các đặc điểm kỹ thuật và hỗ trợ của trình duyệt thay đổi.

Tôi đã đặt mọi thứ lại với nhau trong một số GitHub repository. Hy vọng rằng cách tiếp cận này sẽ tiếp tục hoạt động đáng tin cậy. Dưới đây là example JSFiddle mã đang hoạt động. Trong ví dụ, tất cả các phần tử đang được sắp xếp theo thực tế 'z-index', đó là những gì OP được sau đó.

Kiểm tra và phản hồi về phương pháp này sẽ rất được hoan nghênh!

+0

Tôi sẽ kiểm tra mã của bạn cẩn thận hơn khi tôi có thời gian, nhưng ngay từ cái nhìn đầu tiên có vẻ như là tốt. Tôi chấp nhận câu trả lời của bạn ngay bây giờ, vì đó là câu trả lời hoàn chỉnh nhất cho đến nay. – mgibsonbr

2

Bạn có thể lấy kích thước và độ lệch của các phần tử, sau đó sử dụng document.elementFromPoint() để xác định yếu tố nào được hiển thị ở trên cùng.

+0

Điều này rất hữu ích! Tôi đã hy vọng cho một phương pháp mà không yêu cầu một điểm nhất định, nhưng đối với nhiều mục đích thực tế này thực sự cung cấp một giải pháp tốt. – mgibsonbr

3

Lưu ý: sau hơn một năm mà không có một câu trả lời, câu hỏi này đã also posted at Stack Overflow in Portuguese và - trong khi vẫn không có một giải pháp thuyết phục - một số người dùng và tôi đã có thể replicate the stacking mechanism in JavaScript (reinventing the wheel, nhưng vẫn ...)

Trích dẫn các thuật toán sắp xếp bối cảnh tại (tôi nhấn mạnh) CSS2 specification:

phần tử gốc hình thành bối cảnh xếp chồng gốc. Các bối cảnh xếp chồng khác được tạo ra bởi bất kỳ thành phần nào vị trí (bao gồm các phần tử tương đối vị trí) có giá trị được tính là 'z-index' khác với 'tự động'. Các bối cảnh xếp chồng không nhất thiết liên quan đến các khối chứa.Trong mức độ tương lai của CSS, các tài sản khác có thể giới thiệu bối cảnh xếp chồng, ví dụ 'opacity'

Từ mô tả rằng, đây là một hàm trả về: a) z-index của một phần tử, nếu nó tạo ra một xếp chồng mới contex; hoặc b) undefined nếu không>

function zIndex(ctx) { 
    if (!ctx || ctx === document.body) return; 

    var positioned = css(ctx, 'position') !== 'static'; 
    var hasComputedZIndex = css(ctx, 'z-index') !== 'auto'; 
    var notOpaque = +css(ctx, 'opacity') < 1; 

    if(positioned && hasComputedZIndex) // Ignoring CSS3 for now 
     return +css(ctx, 'z-index'); 
} 

function css(el, prop) { 
    return window.getComputedStyle(el).getPropertyValue(prop); 
} 

Điều này sẽ có thể thiết lập các yếu tố khác nhau tạo thành ngữ cảnh xếp chồng khác nhau. Đối với phần còn lại của các yếu tố (và cho các yếu tố với nhau z-index) các Appendix E nói rằng họ nên tôn trọng "để cây":

Preorder sâu-đầu tiên traversal của cây vẽ, trật tự lôgic (không hình ảnh) cho nội dung hai chiều, sau khi tính đến các thuộc tính tài khoản di chuyển các hộp xung quanh.

Trừ những "tài sản di chuyển hộp xung quanh", chức năng này shoud thực hiện một cách chính xác các traversal:

/* a and b are the two elements we want to compare. 
* ctxA and ctxB are the first noncommon ancestor they have (if any) 
*/ 
function relativePosition(ctxA, ctxB, a, b) { 
    // If one is descendant from the other, the parent is behind (preorder) 
    if ($.inArray(b, $(a).parents()) >= 0) 
     return a; 
    if ($.inArray(a, $(b).parents()) >= 0) 
     return b; 
    // If two contexts are siblings, the one declared first - and all its 
    // descendants (depth first) - is behind 
    return ($(ctxA).index() - $(ctxB).index() > 0 ? a : b); 
} 

Với hai chức năng xác định, chúng tôi cuối cùng có thể tạo chức năng so sánh các yếu tố của chúng tôi:

function inFront(a, b) { 
    // Skip all common ancestors, since no matter its stacking context, 
    // it affects a and b likewise 
    var pa = $(a).parents(), ia = pa.length; 
    var pb = $(b).parents(), ib = pb.length; 
    while (ia >= 0 && ib >= 0 && pa[--ia] == pb[--ib]) { } 

    // Here we have the first noncommon ancestor of a and b 
    var ctxA = (ia >= 0 ? pa[ia] : a), za = zIndex(ctxA); 
    var ctxB = (ib >= 0 ? pb[ib] : b), zb = zIndex(ctxB); 

    // Finds the relative position between them  
    // (this value will only be used if neither has an explicit 
    // and different z-index) 
    var relative = relativePosition(ctxA, ctxB, a, b); 

    // Finds the first ancestor with defined z-index, if any 
    // The "shallowest" one is what matters, since it defined the most general 
    // stacking context (affects all the descendants) 
    while (ctxA && za === undefined) { 
     ctxA = ia < 0 ? null : --ia < 0 ? a : pa[ia]; 
     za = zIndex(ctxA); 
    } 
    while (ctxB && zb === undefined) { 
     ctxB = ib < 0 ? null : --ib < 0 ? b : pb[ib]; 
     zb = zIndex(ctxB); 
    } 

    // Compare the z-indices, if applicable; otherwise use the relative method 
    if (za !== undefined) { 
     if (zb !== undefined) 
      return za > zb ? a : za < zb ? b : relative; 
     return za > 0 ? a : za < 0 ? b : relative; 
    } 
    else if (zb !== undefined) 
     return zb < 0 ? a : zb > 0 ? b : relative; 
    else 
     return relative; 
} 

Dưới đây là ba ví dụ cho thấy phương pháp này trong thực tế: Example 1, Example 2, Example 3 (xin lỗi, không bận tâm dịch mọi thứ sang e ngớ ngẩn ... đó là mã chính xác, chỉ có chức năng khác nhau và tên biến).

Giải pháp này rất có thể chưa hoàn thành và sẽ bị lỗi trong các trường hợp cạnh (mặc dù tôi không thể tìm thấy bất kỳ bản thân nào). Nếu bất cứ ai có bất cứ đề nghị cải tiến, nó sẽ được thực sự đánh giá cao.

Các vấn đề liên quan