jquery.details.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. /*! http://mths.be/details v0.0.6 by @mathias | includes http://mths.be/noselect v1.0.3 */
  2. define([
  3. "jquery",
  4. "modernizr/modernizr.details"
  5. ], function($){
  6. var proto = $.fn,
  7. details,
  8. // :'(
  9. isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]',
  10. // Feature test for native `<details>` support
  11. isDetailsSupported = $('html').hasClass('details'),
  12. toggleOpen = function($details, $detailsSummary, $detailsNotSummary, toggle) {
  13. var isOpen = typeof $details.attr('open') == 'string',
  14. close = isOpen && toggle || !isOpen && !toggle;
  15. if (close) {
  16. $details.removeClass('open').prop('open', false).triggerHandler('close.details');
  17. $detailsSummary.attr('aria-expanded', false);
  18. $detailsNotSummary.hide();
  19. } else {
  20. $details.addClass('open').prop('open', true).triggerHandler('open.details');
  21. $detailsSummary.attr('aria-expanded', true);
  22. $detailsNotSummary.show();
  23. }
  24. };
  25. /* http://mths.be/noselect v1.0.3 */
  26. proto.noSelect = function() {
  27. // Since the string 'none' is used three times, storing it in a variable gives better results after minification
  28. var none = 'none';
  29. // onselectstart and ondragstart for WebKit & IE
  30. // onmousedown for WebKit & Opera
  31. return this.bind('selectstart dragstart mousedown', function() {
  32. return false;
  33. }).css({
  34. 'MozUserSelect': none,
  35. 'msUserSelect': none,
  36. 'webkitUserSelect': none,
  37. 'userSelect': none
  38. });
  39. };
  40. // Execute the fallback only if there’s no native `details` support
  41. if (isDetailsSupported) {
  42. details = proto.details = function() {
  43. return this.each(function() {
  44. var $details = $(this),
  45. $summary = $('summary', $details).first();
  46. $summary.attr({
  47. 'role': 'button',
  48. 'aria-expanded': $details.prop('open')
  49. }).on('click.xxx', function() {
  50. // the value of the `open` property is the old value
  51. var close = $details.prop('open');
  52. $summary.attr('aria-expanded', !close);
  53. $details.triggerHandler((close ? 'close' : 'open') + '.details');
  54. });
  55. });
  56. };
  57. details.support = isDetailsSupported;
  58. } else {
  59. details = proto.details = function() {
  60. // Loop through all `details` elements
  61. return this.each(function() {
  62. // Store a reference to the current `details` element in a variable
  63. var $details = $(this),
  64. // Store a reference to the `summary` element of the current `details` element (if any) in a variable
  65. $detailsSummary = $('summary', $details).first(),
  66. // Do the same for the info within the `details` element
  67. $detailsNotSummary = $details.children(':not(summary)'),
  68. // This will be used later to look for direct child text nodes
  69. $detailsNotSummaryContents = $details.contents(':not(summary)');
  70. // If there is no `summary` in the current `details` element…
  71. if (!$detailsSummary.length) {
  72. // …create one with default text
  73. $detailsSummary = $('<summary>').text($.mage.__('Details')).prependTo($details);
  74. }
  75. // Look for direct child text nodes
  76. if ($detailsNotSummary.length != $detailsNotSummaryContents.length) {
  77. // Wrap child text nodes in a `span` element
  78. $detailsNotSummaryContents.filter(function() {
  79. // Only keep the node in the collection if it’s a text node containing more than only whitespace
  80. // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#space-character
  81. return this.nodeType == 3 && /[^ \t\n\f\r]/.test(this.data);
  82. }).wrap('<span>');
  83. // There are now no direct child text nodes anymore — they’re wrapped in `span` elements
  84. $detailsNotSummary = $details.children(':not(summary)');
  85. }
  86. // Hide content unless there’s an `open` attribute
  87. toggleOpen($details, $detailsSummary, $detailsNotSummary);
  88. // Add `role=button` and set the `tabindex` of the `summary` element to `0` to make it keyboard accessible
  89. $detailsSummary.attr('role', 'button').noSelect().prop('tabIndex', 0).off('click').on('click.details', function() {
  90. // Focus on the `summary` element
  91. $detailsSummary.focus();
  92. // Toggle the `open` and `aria-expanded` attributes and the `open` property of the `details` element and display the additional info
  93. toggleOpen($details, $detailsSummary, $detailsNotSummary, true);
  94. }).keyup(function(event) {
  95. if (32 == event.keyCode || (13 == event.keyCode && !isOpera)) {
  96. // Space or Enter is pressed — trigger the `click` event on the `summary` element
  97. // Opera already seems to trigger the `click` event when Enter is pressed
  98. event.preventDefault();
  99. $detailsSummary.click();
  100. }
  101. });
  102. });
  103. };
  104. details.support = isDetailsSupported;
  105. }
  106. });