diff --git a/css/jquery-comments.css b/css/jquery-comments.css index fed5aba..bb3d12a 100644 --- a/css/jquery-comments.css +++ b/css/jquery-comments.css @@ -10,8 +10,16 @@ http://viima.github.io/jquery-comments/*/ text-shadow: none; } +.jquery-comments input, +.jquery-comments select, +.jquery-comments button, +.jquery-comments textarea { + font-family: 'Arial'; + font-size: 1em; +} + .jquery-comments a[href]:not(.tag) { - color: #2793e6; + color: #0a6ab2; text-decoration: none; } @@ -90,7 +98,7 @@ http://viima.github.io/jquery-comments/*/ .jquery-comments .tag:not(.deletable):hover { background-color: #d8edf8; - border-color: #2793e6; + border-color: #0a6ab2; } .jquery-comments [contentEditable=true]:empty:not(:focus):before{ @@ -232,6 +240,8 @@ http://viima.github.io/jquery-comments/*/ } .jquery-comments .textarea-wrapper .close { + position: absolute; + top: 0; width: 1em; height: 1em; } @@ -259,7 +269,8 @@ http://viima.github.io/jquery-comments/*/ padding-top: .3em; } -.jquery-comments .textarea-wrapper .control-row > span { +.jquery-comments .textarea-wrapper .control-row > span, +.jquery-comments .textarea-wrapper .control-row > button { float: right; line-height: 1.6em; margin-top: .4em; @@ -270,47 +281,53 @@ http://viima.github.io/jquery-comments/*/ opacity: .5; } -.jquery-comments .textarea-wrapper .control-row > span:not(:first-child) { +.jquery-comments .textarea-wrapper .control-row > *:not(:first-child) { margin-right: .5em; } -.jquery-comments .textarea-wrapper .control-row > span.enabled { +.jquery-comments .textarea-wrapper .control-row > .enabled { opacity: 1; cursor: pointer; } -.jquery-comments .textarea-wrapper .control-row > span:not(.enabled) { +.jquery-comments .textarea-wrapper .control-row > span:not(.enabled), +.jquery-comments .textarea-wrapper .control-row > button:not(.enabled) { pointer-events: none; } -.jquery-comments .textarea-wrapper .control-row > span.enabled:hover { +.jquery-comments .textarea-wrapper .control-row > .enabled:hover { opacity: .9; } -.jquery-comments .textarea-wrapper .control-row > span.upload { +.jquery-comments .textarea-wrapper .control-row > .upload { position: relative; overflow: hidden; - background-color: #999; + background-color: #575757; +} + +.jquery-comments .textarea-wrapper .control-row > .upload.focus { + outline: 2px dotted #0a6ab2; } -.jquery-comments ul.navigation { +.jquery-comments .navigation { clear: both; - color: #999; + color: #575757; border-bottom: 2px solid #CCC; line-height: 2em; font-size: 1em; margin-bottom: 0.5em; } -.jquery-comments ul.navigation .navigation-wrapper { +.jquery-comments .navigation .navigation-wrapper { position: relative; } -.jquery-comments ul.navigation li { +.jquery-comments .navigation button { display: inline-block; position: relative; padding: 0 1em; + line-height: 2em; cursor: pointer; text-align: center; @@ -320,12 +337,12 @@ http://viima.github.io/jquery-comments/*/ user-select: none; } -.jquery-comments ul.navigation li.active, -.jquery-comments ul.navigation li:hover { +.jquery-comments .navigation button.active, +.jquery-comments .navigation button:hover { color: #000; } -.jquery-comments ul.navigation li.active:after { +.jquery-comments .navigation button.active:after { content: " "; display: block; right: 0; @@ -336,39 +353,39 @@ http://viima.github.io/jquery-comments/*/ left: 0; } -.jquery-comments ul.navigation li[data-sort-key="attachments"] { +.jquery-comments .navigation button[data-sort-key="attachments"] { float: right; } -.jquery-comments ul.navigation li[data-sort-key="attachments"] i { +.jquery-comments .navigation button[data-sort-key="attachments"] i { margin-right: 0.25em; } -.jquery-comments ul.navigation .navigation-wrapper.responsive { +.jquery-comments .navigation .navigation-wrapper.responsive { display: none; } @media screen and (max-width: 600px) { - .jquery-comments ul.navigation .navigation-wrapper { + .jquery-comments .navigation .navigation-wrapper { display: none; } - .jquery-comments ul.navigation .navigation-wrapper.responsive { + .jquery-comments .navigation .navigation-wrapper.responsive { display: inline; } } -.jquery-comments.responsive ul.navigation .navigation-wrapper { +.jquery-comments.responsive .navigation .navigation-wrapper { display: none; } -.jquery-comments.responsive ul.navigation .navigation-wrapper.responsive { +.jquery-comments.responsive .navigation .navigation-wrapper.responsive { display: inline; } -.jquery-comments ul.navigation .navigation-wrapper.responsive li.title { +.jquery-comments .navigation .navigation-wrapper.responsive li.title { padding: 0 1.5em; } -.jquery-comments ul.navigation .navigation-wrapper.responsive li.title header:after { +.jquery-comments .navigation .navigation-wrapper.responsive li.title header:after { display: inline-block; content: ""; border-left: 0.3em solid rgba(0, 0, 0, 0) !important; @@ -379,8 +396,8 @@ http://viima.github.io/jquery-comments/*/ top: -0.1em; } -.jquery-comments ul.navigation .navigation-wrapper.responsive li.title.active header:after, -.jquery-comments ul.navigation .navigation-wrapper.responsive li.title:hover header:after { +.jquery-comments .navigation .navigation-wrapper.responsive li.title.active header:after, +.jquery-comments .navigation .navigation-wrapper.responsive li.title:hover header:after { border-top-color: #000; } @@ -441,7 +458,7 @@ http://viima.github.io/jquery-comments/*/ } .jquery-comments ul.dropdown li .email { - color: #999; + color: #575757; font-size: 0.95em; margin-top: 0.1em; } @@ -524,11 +541,14 @@ http://viima.github.io/jquery-comments/*/ } .jquery-comments ul.main li.comment .comment-header .name { + margin: 0 0.5rem 0 0; + font-size: 1em; + display: inline-block; font-weight: bold; } .jquery-comments ul.main li.comment .comment-header .reply-to { - color: #999; + color: #575757; font-size: .8em; font-weight: normal; vertical-align: top; @@ -539,7 +559,7 @@ http://viima.github.io/jquery-comments/*/ } .jquery-comments ul.main li.comment .comment-header .new { - background: #2793e6; + background: #0a6ab2; font-size: 0.8em; padding: 0.2em 0.6em; color: #fff; @@ -569,7 +589,7 @@ http://viima.github.io/jquery-comments/*/ margin: 0; font-size: .9em; font-style: italic; - color: #999; + color: #575757; } .jquery-comments ul.main li.comment .wrapper .content time.edited:before { @@ -604,7 +624,7 @@ http://viima.github.io/jquery-comments/*/ } .jquery-comments ul.main li.comment .actions > * { - color: #999; + color: #575757; font-weight: bold; } @@ -684,20 +704,21 @@ http://viima.github.io/jquery-comments/*/ font-size: 2.4em; } -.jquery-comments ul.main li.comment .child-comments li.toggle-all { - padding-top: 0; +.jquery-comments ul.main li.comment .toggle-all { + margin-left: calc(3.6em + .5em); + padding: .5em; } -.jquery-comments ul.main li.comment .child-comments li.toggle-all span:first-child { +.jquery-comments ul.main li.comment .toggle-all span:first-child { vertical-align: middle; } -.jquery-comments ul.main li.comment .child-comments li.toggle-all span:first-child:hover { +.jquery-comments ul.main li.comment .toggle-all span:first-child:hover { cursor: pointer; text-decoration: underline; } -.jquery-comments ul.main li.comment .child-comments li.toggle-all .caret { +.jquery-comments ul.main li.comment .toggle-all .caret { display: inline-block; vertical-align: middle; width: 0; @@ -712,7 +733,7 @@ http://viima.github.io/jquery-comments/*/ border-right-color: rgba(0, 0, 0, 0); } -.jquery-comments ul.main li.comment .child-comments li.toggle-all .caret.up { +.jquery-comments ul.main li.comment .toggle-all .caret.up { border-top-color: rgba(0, 0, 0, 0); border-bottom-color: inherit; margin-top: -.2em; @@ -770,7 +791,7 @@ http://viima.github.io/jquery-comments/*/ } .jquery-comments .droppable-overlay .droppable-container .droppable.drag-over { - color: #999; + color: #575757; } .jquery-comments .droppable-overlay .droppable-container .droppable i { @@ -784,3 +805,24 @@ http://viima.github.io/jquery-comments/*/ .jquery-comments.read-only .actions { display: none; } + +/* Accessibility */ +input:focus, input:active, +textarea:focus, textarea:active +select:focus, select:active, +button:focus, button:active, +a:focus, a:active { + outline: 2px dotted #0a6ab2; +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} \ No newline at end of file diff --git a/js/jquery-comments.js b/js/jquery-comments.js index 1814098..73323ee 100644 --- a/js/jquery-comments.js +++ b/js/jquery-comments.js @@ -62,11 +62,12 @@ 'blur [contenteditable]' : 'checkEditableContentForChange', // Navigation - 'click .navigation li[data-sort-key]' : 'navigationElementClicked', + 'click .navigation button[data-sort-key]' : 'navigationElementClicked', 'click .navigation li.title' : 'toggleNavigationDropdown', // Main comenting field - 'click .commenting-field.main .textarea': 'showMainCommentingField', + 'focus .commenting-field.main .textarea': 'showMainCommentingField', + 'click .commenting-field.main .textarea': 'showMainCommentingFieldClick', 'click .commenting-field.main .close' : 'hideMainCommentingField', // All commenting fields @@ -88,7 +89,7 @@ 'click li.comment .ping' : 'pingClicked', // Other - 'click li.comment ul.child-comments .toggle-all': 'toggleReplies', + 'click li.comment .toggle-all': 'toggleReplies', 'click li.comment button.reply': 'replyButtonClicked', 'click li.comment button.edit': 'editButtonClicked', @@ -107,6 +108,10 @@ 'click .dropdown.autocomplete': 'stopPropagation', 'mousedown .dropdown.autocomplete': 'stopPropagation', 'touchstart .dropdown.autocomplete': 'stopPropagation', + + // Accessibility + 'focus #file-upload-main': 'handleFocusAttachment', + 'blur #file-upload-main': 'handleBlurAttachment' }, @@ -138,7 +143,7 @@ attachmentsText: 'Attachments', sendText: 'Send', replyText: 'Reply', - editText: 'Edit', + editText: 'Edit this comment', editedText: 'Edited', youText: 'You', saveText: 'Save', @@ -167,8 +172,8 @@ defaultNavigationSortKey: 'newest', // Colors - highlightColor: '#2793e6', - deleteButtonColor: '#C9302C', + highlightColor: '#0a6ab2', + deleteButtonColor: '#575757', scrollContainer: this.$el, roundProfilePictures: false, @@ -205,7 +210,23 @@ hashtagClicked: function(hashtag) {}, pingClicked: function(userId) {}, refresh: function() {}, - timeFormatter: function(time) {return new Date(time).toLocaleDateString()} + timeFormatter: function(time) {return new Date(time).toLocaleDateString()}, + + // Accessibility + replyToText: 'Replied to ', + upvoteText: 'Upvote this comment', + downvoteText: 'Downvote this comment', + commentUpvoted: 'The comment has been upvoted.', + commentDownvoted: 'The comment has been downvoted.', + likedText: ' people upvoted this comment', + deleteAttachmentText: 'Delete this attachment', + closeButtonText: 'Clear this comment', + message: '.js-message', + commentAddedLoading: 'Your comment is being added.', + commentAdded: 'Your comment has been added.', + commentUpdated: 'Your comment has been updated.', + commentDeletedLoading: 'Your comment is being deleted.', + commentDeleted: 'Your comment has been deleted.', } }, @@ -593,7 +614,7 @@ var childCommentsEl = parentEl.find('.child-comments'); var childComments = childCommentsEl.find('.comment').not('.hidden'); - var toggleAllButton = childCommentsEl.find('li.toggle-all'); + var toggleAllButton = parentEl.find('.toggle-all'); childComments.removeClass('togglable-reply'); // Select replies to be hidden @@ -617,8 +638,10 @@ // Append button to toggle all replies if necessary if(!toggleAllButton.length) { - toggleAllButton = $('
', { - 'class': 'toggle-all highlight-font-bold' + toggleAllButton = $('', { + 'class': 'toggle-all highlight-font-bold', + 'aria-expanded': false, + 'aria-controls': childCommentsEl.attr('id'), }); var toggleAllButtonText = $('', { 'class': 'text' @@ -629,7 +652,7 @@ // Append toggle button to DOM toggleAllButton.append(toggleAllButtonText).append(caret); - childCommentsEl.prepend(toggleAllButton); + toggleAllButton.insertBefore(childCommentsEl); } // Update the text of toggle all -button @@ -706,11 +729,11 @@ }, showActiveSort: function() { - var activeElements = this.$el.find('.navigation li[data-sort-key="' + this.currentSortKey + '"]'); + var activeElements = this.$el.find('.navigation button[data-sort-key="' + this.currentSortKey + '"]'); // Indicate active sort - this.$el.find('.navigation li').removeClass('active'); - activeElements.addClass('active'); + this.$el.find('.navigation button').removeClass('active').attr('aria-selected', 'false'); + activeElements.addClass('active').attr('aria-selected', 'true'); // Update title for dropdown var titleEl = this.$el.find('.navigation .title'); @@ -813,6 +836,14 @@ }, showMainCommentingField: function(ev) { + var mainTextarea = $(ev.currentTarget); + mainTextarea.siblings('.control-row').show(); + mainTextarea.parent().find('.close').show(); + mainTextarea.parent().find('.upload.inline-button').hide(); + //mainTextarea.focus(); + }, + + showMainCommentingFieldClick: function(ev) { var mainTextarea = $(ev.currentTarget); mainTextarea.siblings('.control-row').show(); mainTextarea.parent().find('.close').show(); @@ -841,7 +872,7 @@ mainControlRow.hide(); closeButton.hide(); mainTextarea.parent().find('.upload.inline-button').show(); - mainTextarea.blur(); + mainTextarea.text('').focus(); }, increaseTextareaHeight: function(ev) { @@ -941,6 +972,9 @@ var sendButton = $(ev.currentTarget); var commentingField = sendButton.parents('.commenting-field').first(); + // Accessibility, add retroaction message + $(self.options.message).text(self.options.commentAddedLoading); + // Set button state to loading this.setButtonState(sendButton, false, true); @@ -956,6 +990,10 @@ // Reset button state self.setButtonState(sendButton, false, false); + + // Accessibility, add retroaction message + $(commentingField).find('.send').focus(); + $(self.options.message).text(self.options.commentAdded); }; var error = function() { @@ -986,6 +1024,7 @@ var saveButton = $(ev.currentTarget); var commentingField = saveButton.parents('.commenting-field').first(); var textarea = commentingField.find('.textarea'); + var id = textarea.attr('data-comment'); // Set button state to loading this.setButtonState(saveButton, false, true); @@ -1021,6 +1060,10 @@ // Reset button state self.setButtonState(saveButton, false, false); + + // Accessibility, add retroaction message and put focus on the comment + $(self.options.message).text(self.options.commentUpdated); + $('li[data-id="' + id + '"]').attr('tabindex', '1').focus(); }; var error = function() { @@ -1039,19 +1082,39 @@ var commentJSON = $.extend({}, this.commentsById[commentEl.attr('data-id')]); var commentId = commentJSON.id; var parentId = commentJSON.parent; + var afterFocusId; + + console.log('commentId', commentId) + console.log('parentId', parentId) // Set button state to loading this.setButtonState(deleteButton, false, true); + // Accessibility, add retroaction message and put focus on the comment + $(self.options.message).text(self.options.commentDeletedLoading); + // Reverse mapping commentJSON = this.applyExternalMappings(commentJSON); var success = function() { + if (!parentId) { + if ($('li[data-id="' + commentId + '"]').prev().length > 0) { + afterFocusId = $('li[data-id="' + commentId + '"]').prev().data('id'); + } else { + afterFocusId = $('li[data-id="' + commentId + '"]').next().data('id'); + } + } else { + afterFocusId = parentId; + } self.removeComment(commentId); if(parentId) self.reRenderCommentActionBar(parentId); // Reset button state self.setButtonState(deleteButton, false, false); + + // Accessibility, add retroaction message and put focus on the comment + $(self.options.message).text(self.options.commentDeleted); + $('li[data-id="' + afterFocusId + '"]').focus(); }; var error = function() { @@ -1084,6 +1147,7 @@ upvoteComment: function(ev) { var self = this; var commentEl = $(ev.currentTarget).parents('li.comment').first(); + var id = $(commentEl).data('id'); var commentModel = commentEl.data().model; // Check whether user upvoted the comment or revoked the upvote @@ -1104,10 +1168,18 @@ var commentJSON = $.extend({}, commentModel); commentJSON = this.applyExternalMappings(commentJSON); + // Accessibility, get the right message + var message = commentModel.userHasUpvoted ? this.options.commentUpvoted : this.options.commentDownvoted; + var success = function(commentJSON) { var commentModel = self.createCommentModel(commentJSON); self.updateCommentModel(commentModel); self.reRenderUpvotes(commentModel.id); + + // Accessibility, output the message to the screen reader + $('li.comment[data-id="' + id +'"]').find('.action.upvote').first().focus(); + + $(self.options.message).text(message); }; var error = function() { @@ -1116,6 +1188,9 @@ commentModel.userHasUpvoted = !commentModel.userHasUpvoted; commentModel.upvoteCount = previousUpvoteCount; self.reRenderUpvotes(commentModel.id); + + // Accessibility, output the message to the screen reader + $(self.options.message).text(message); }; this.options.upvoteComment(commentJSON, success, error); @@ -1123,7 +1198,7 @@ toggleReplies: function(ev) { var el = $(ev.currentTarget); - el.siblings('.togglable-reply').toggleClass('visible'); + el.siblings('.child-comments').find('.togglable-reply').toggleClass('visible'); this.setToggleAllButtonText(el, true); }, @@ -1239,6 +1314,16 @@ ev.stopPropagation(); }, + // Accessibility + handleFocusAttachment: function(ev) { + $(ev.target).parents('.upload').addClass('focus'); + }, + + handleBlurAttachment: function(ev) { + $(ev.target).parents('.upload').removeClass('focus'); + }, + + // HTML elements // ============= @@ -1246,6 +1331,13 @@ createHTML: function() { var self = this; + // Accesibility, add a div for messaging + var messageField = $('', { + 'class': 'sr-only js-message', + 'aria-live': 'polite' + }); + this.$el.append(messageField); + // Commenting field var mainCommentingField = this.createMainCommentingFieldElement(); this.$el.append(mainCommentingField); @@ -1268,7 +1360,8 @@ // Comments container var commentsContainer = $('', { 'class': 'data-container', - 'data-container': 'comments' + 'data-container': 'comments', + //'aria-live': 'polite', }); this.$el.append(commentsContainer); @@ -1407,6 +1500,9 @@ var textarea = $('', { 'class': 'textarea', 'data-placeholder': this.options.textFormatter(this.options.textareaPlaceholderText), + 'role': 'textarea', + 'aria-multiline': true, + 'aria-label': this.options.textareaPlaceholderText, contenteditable: true }); @@ -1420,7 +1516,7 @@ // Save button var saveButtonClass = existingCommentId ? 'update' : 'send'; var saveButtonText = existingCommentId ? this.options.textFormatter(this.options.saveText) : this.options.textFormatter(this.options.sendText); - var saveButton = $('', { + var saveButton = $('', { 'class': saveButtonClass + ' save highlight-background', 'text': saveButtonText }); @@ -1432,7 +1528,7 @@ // Delete button var deleteButtonText = this.options.textFormatter(this.options.deleteText); - var deleteButton = $('', { + var deleteButton = $('', { 'class': 'delete enabled', text: deleteButtonText }).css('background-color', this.options.deleteButtonColor); @@ -1451,8 +1547,13 @@ var uploadIcon = $('', { 'class': 'fa fa-paperclip' }); + var labelFileInput = $('', { + 'for': 'file-upload-main', + 'html': 'Upload an attachment to your comment', + }); var fileInput = $('', { 'type': 'file', + 'id': 'file-upload-main', 'multiple': 'multiple', 'data-role': 'none' // Prevent jquery-mobile for adding classes }); @@ -1461,7 +1562,7 @@ uploadIcon.css('background-image', 'url("'+this.options.uploadIconURL+'")'); uploadIcon.addClass('image'); } - uploadButton.append(uploadIcon).append(fileInput); + uploadButton.append(uploadIcon).append(labelFileInput).append(fileInput); // Main upload button var mainUploadButton = uploadButton.clone(); @@ -1488,7 +1589,7 @@ // Populate the element - textareaWrapper.append(closeButton).append(textarea).append(controlRow); + textareaWrapper.append(textarea).append(closeButton).append(controlRow); commentingField.append(profilePicture).append(textareaWrapper); @@ -1640,40 +1741,50 @@ }, createNavigationElement: function() { - var navigationEl = $('