开发者

Implementing a jQuery fallback for the details element

I'm trying to implement a jQuery fallback for the details element. If you've never heard about it, it is basically a Disclosure widget. If the boolean attribute open is present, it indicates that both the summary and the additional information is to be shown to the user. If the attribute is absent, only the summary is to be shown. The following HTML and CSS achieve that.

HTML

<!-- opened -->
<details open>
    <summary>Summary</summary>
    <p>Additional information</p>
</details>

<!-- closed -->
<details>
    <summary>Summary</summary>
    <p>开发者_运维百科Additional information</p>
</details>

CSS

details summary ~ * {
    display: none;
}

details[open] summary ~ * {
    display: block;
}

I then added the following jQuery to add/remove the open attribute when the summary element is clicked.

jQuery

$("summary").click(function() {
    if ($(this).parent().attr("open")) {
        $(this).parent().removeAttr("open");
    } else {
        $(this).parent().attr("open", "open");
    }
});

It adds and removes the open attribute, however the p element's visibility remains unaffected in Chrome. What am I doing wrong? Here is a live example.

Updates

  • It works in Firefox 4.
  • manjii pointed out that open should be changed to open="open" or it will not work the first time. BoltClock also provided an alternative solution. This is not the main issue though.
  • marcosfromero and BoltClock brought up the issue of Chrome's dynamic styles support, which I think could be related.


It turned out to be a WebKit bug reported here.

Bug 21346 attribute value selector not being reevaluated upon attribute change

Adding this empty rule will temporarily fix the issue I was having:

details[open] {}

Contrary to the description in the bug report, it seems to occur when using an attribute selector followed by a descendant combinator.

However, Chrome 12 was released today and it has native support for the details and summary elements.


It's not working only the first time, because for:

<details open>

$(this).parent().attr("open") is equal to "" (empty string) = false => the attribute is not removed but given the value "open" => second time clicked will work.

To fix this, add a value to the attribute:

<details open="open">


Add Mozilla Firefox support

<details>
<summary>Summary</summary>
<p id="detail">Additional information</p>
</details>


<script>
var ischrome = false;
if(/chrom(e|ium)/.test(navigator.userAgent.toLowerCase())){
    ischrome = true;
    }

$detail = $('#detail');
$detail.hide();
if(!ischrome){
    $('summary').prepend('► ');                     
}
$('summary').on('click', function(e){
    if ($detail.is(":visible")){
        $('summary').html('► summary');
        $detail.hide();                                 
    }else{              
        $('summary').html('▼ summary');             
        $detail.show(); 
    }
});                     
</script>


At least in Chrome, the problem is not with the jQuery code but with Chrome support for your CSS selectors.

If you debug the jQuery code, it works, but CSS is not applied as intended.

Check this fork of your original code with a "debug console":

http://jsfiddle.net/marcosfromero/KQGn8/


In Safari you will need to add evt.preventDefault() to the click handler, otherwise it will also trigger the native behaviour, returning it to the state it was at before the click (this doesn't seem to happen in Chrome, oddly).

$("summary").click(function(evt) {
    if ($(this).parent().attr("open")) {
        $(this).parent().removeAttr("open");
    } else {
        $(this).parent().attr("open", "open");
    }
    evt.preventDefault();
});


I use this solution to get it to work on all non-supported browsers. It does not require nor use jQuery, but you can enhance it with jQuery if you like (it is just plain JS).

Here is the solution in action:

details summary {display: block;}    
details summary ~ * {
    display: none;
}
details[open] summary ~ * {
    display: block;
}
<details>
  <summary onclick="if(this.parentElement.getAttribute('open')!='open') this.parentElement.setAttribute('open','open'); else this.parentElement.removeAttribute ('open'); return false;">{{ item.title }}</summary>
  {{ item.content }}
</details>

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜