[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/js/ -> comment-reply.js (source)

   1  /**
   2   * Handles the addition of the comment form.
   3   *
   4   * @since 2.7.0
   5   * @output wp-includes/js/comment-reply.js
   6   *
   7   * @namespace addComment
   8   *
   9   * @type {Object}
  10   */
  11  window.addComment = ( function( window ) {
  12      // Avoid scope lookups on commonly used variables.
  13      var document = window.document;
  14  
  15      // Settings.
  16      var config = {
  17          commentReplyClass   : 'comment-reply-link',
  18          commentReplyTitleId : 'reply-title',
  19          cancelReplyId       : 'cancel-comment-reply-link',
  20          commentFormId       : 'commentform',
  21          temporaryFormId     : 'wp-temp-form-div',
  22          parentIdFieldId     : 'comment_parent',
  23          postIdFieldId       : 'comment_post_ID'
  24      };
  25  
  26      // Cross browser MutationObserver.
  27      var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
  28  
  29      // Check browser cuts the mustard.
  30      var cutsTheMustard = 'querySelector' in document && 'addEventListener' in window;
  31  
  32      /*
  33       * Check browser supports dataset.
  34       * !! sets the variable to true if the property exists.
  35       */
  36      var supportsDataset = !! document.documentElement.dataset;
  37  
  38      // For holding the cancel element.
  39      var cancelElement;
  40  
  41      // For holding the comment form element.
  42      var commentFormElement;
  43  
  44      // The respond element.
  45      var respondElement;
  46  
  47      // The mutation observer.
  48      var observer;
  49  
  50      if ( cutsTheMustard && document.readyState !== 'loading' ) {
  51          ready();
  52      } else if ( cutsTheMustard ) {
  53          window.addEventListener( 'DOMContentLoaded', ready, false );
  54      }
  55  
  56      /**
  57       * Sets up object variables after the DOM is ready.
  58       *
  59       * @since 5.1.1
  60       */
  61  	function ready() {
  62          // Initialize the events.
  63          init();
  64  
  65          // Set up a MutationObserver to check for comments loaded late.
  66          observeChanges();
  67      }
  68  
  69      /**
  70       * Add events to links classed .comment-reply-link.
  71       *
  72       * Searches the context for reply links and adds the JavaScript events
  73       * required to move the comment form. To allow for lazy loading of
  74       * comments this method is exposed as window.commentReply.init().
  75       *
  76       * @since 5.1.0
  77       *
  78       * @memberOf addComment
  79       *
  80       * @param {HTMLElement} context The parent DOM element to search for links.
  81       */
  82  	function init( context ) {
  83          if ( ! cutsTheMustard ) {
  84              return;
  85          }
  86  
  87          // Get required elements.
  88          cancelElement = getElementById( config.cancelReplyId );
  89          commentFormElement = getElementById( config.commentFormId );
  90  
  91          // No cancel element, no replies.
  92          if ( ! cancelElement ) {
  93              return;
  94          }
  95  
  96          cancelElement.addEventListener( 'click', cancelEvent );
  97  
  98          // Submit the comment form when the user types [Ctrl] or [Cmd] + [Enter].
  99          var submitFormHandler = function( e ) {
 100              if ( ( e.metaKey || e.ctrlKey ) && e.keyCode === 13 && document.activeElement.tagName.toLowerCase() !== 'a' ) {
 101                  commentFormElement.removeEventListener( 'keydown', submitFormHandler );
 102                  e.preventDefault();
 103                  // The submit button ID is 'submit' so we can't call commentFormElement.submit(). Click it instead.
 104                  commentFormElement.submit.click();
 105                  return false;
 106              }
 107          };
 108  
 109          if ( commentFormElement ) {
 110              commentFormElement.addEventListener( 'keydown', submitFormHandler );
 111          }
 112  
 113          var links = replyLinks( context );
 114          var element;
 115  
 116          for ( var i = 0, l = links.length; i < l; i++ ) {
 117              element = links[i];
 118  
 119              element.addEventListener( 'click', clickEvent );
 120          }
 121      }
 122  
 123      /**
 124       * Return all links classed .comment-reply-link.
 125       *
 126       * @since 5.1.0
 127       *
 128       * @param {HTMLElement} context The parent DOM element to search for links.
 129       *
 130       * @return {HTMLCollection|NodeList|Array}
 131       */
 132  	function replyLinks( context ) {
 133          var selectorClass = config.commentReplyClass;
 134          var allReplyLinks;
 135  
 136          // childNodes is a handy check to ensure the context is a HTMLElement.
 137          if ( ! context || ! context.childNodes ) {
 138              context = document;
 139          }
 140  
 141          if ( document.getElementsByClassName ) {
 142              // Fastest.
 143              allReplyLinks = context.getElementsByClassName( selectorClass );
 144          }
 145          else {
 146              // Fast.
 147              allReplyLinks = context.querySelectorAll( '.' + selectorClass );
 148          }
 149  
 150          return allReplyLinks;
 151      }
 152  
 153      /**
 154       * Cancel event handler.
 155       *
 156       * @since 5.1.0
 157       *
 158       * @param {Event} event The calling event.
 159       */
 160  	function cancelEvent( event ) {
 161          var cancelLink = this;
 162          var temporaryFormId  = config.temporaryFormId;
 163          var temporaryElement = getElementById( temporaryFormId );
 164  
 165          if ( ! temporaryElement || ! respondElement ) {
 166              // Conditions for cancel link fail.
 167              return;
 168          }
 169  
 170          getElementById( config.parentIdFieldId ).value = '0';
 171  
 172          // Move the respond form back in place of the temporary element.
 173          var headingText = temporaryElement.textContent;
 174          temporaryElement.parentNode.replaceChild( respondElement, temporaryElement );
 175          cancelLink.style.display = 'none';
 176  
 177          var replyHeadingElement  = getElementById( config.commentReplyTitleId );
 178          var replyHeadingTextNode = replyHeadingElement && replyHeadingElement.firstChild;
 179          var replyLinkToParent    = replyHeadingTextNode && replyHeadingTextNode.nextSibling;
 180  
 181          if ( replyHeadingTextNode && replyHeadingTextNode.nodeType === Node.TEXT_NODE && headingText ) {
 182              if ( replyLinkToParent && 'A' === replyLinkToParent.nodeName && replyLinkToParent.id !== config.cancelReplyId ) {
 183                  replyLinkToParent.style.display = '';
 184              }
 185  
 186              replyHeadingTextNode.textContent = headingText;
 187          }
 188  
 189          event.preventDefault();
 190      }
 191  
 192      /**
 193       * Click event handler.
 194       *
 195       * @since 5.1.0
 196       *
 197       * @param {Event} event The calling event.
 198       */
 199  	function clickEvent( event ) {
 200          var replyNode = getElementById( config.commentReplyTitleId );
 201          var defaultReplyHeading = replyNode && replyNode.firstChild.textContent;
 202          var replyLink = this,
 203              commId    = getDataAttribute( replyLink, 'belowelement' ),
 204              parentId  = getDataAttribute( replyLink, 'commentid' ),
 205              respondId = getDataAttribute( replyLink, 'respondelement' ),
 206              postId    = getDataAttribute( replyLink, 'postid' ),
 207              replyTo   = getDataAttribute( replyLink, 'replyto' ) || defaultReplyHeading,
 208              follow;
 209  
 210          if ( ! commId || ! parentId || ! respondId || ! postId ) {
 211              /*
 212               * Theme or plugin defines own link via custom `wp_list_comments()` callback
 213               * and calls `moveForm()` either directly or via a custom event hook.
 214               */
 215              return;
 216          }
 217  
 218          /*
 219           * Third party comments systems can hook into this function via the global scope,
 220           * therefore the click event needs to reference the global scope.
 221           */
 222          follow = window.addComment.moveForm( commId, parentId, respondId, postId, replyTo );
 223          if ( false === follow ) {
 224              event.preventDefault();
 225          }
 226      }
 227  
 228      /**
 229       * Creates a mutation observer to check for newly inserted comments.
 230       *
 231       * @since 5.1.0
 232       */
 233  	function observeChanges() {
 234          if ( ! MutationObserver ) {
 235              return;
 236          }
 237  
 238          var observerOptions = {
 239              childList: true,
 240              subtree: true
 241          };
 242  
 243          observer = new MutationObserver( handleChanges );
 244          observer.observe( document.body, observerOptions );
 245      }
 246  
 247      /**
 248       * Handles DOM changes, calling init() if any new nodes are added.
 249       *
 250       * @since 5.1.0
 251       *
 252       * @param {Array} mutationRecords Array of MutationRecord objects.
 253       */
 254  	function handleChanges( mutationRecords ) {
 255          var i = mutationRecords.length;
 256  
 257          while ( i-- ) {
 258              // Call init() once if any record in this set adds nodes.
 259              if ( mutationRecords[ i ].addedNodes.length ) {
 260                  init();
 261                  return;
 262              }
 263          }
 264      }
 265  
 266      /**
 267       * Backward compatible getter of data-* attribute.
 268       *
 269       * Uses element.dataset if it exists, otherwise uses getAttribute.
 270       *
 271       * @since 5.1.0
 272       *
 273       * @param {HTMLElement} Element DOM element with the attribute.
 274       * @param {string}      Attribute the attribute to get.
 275       *
 276       * @return {string}
 277       */
 278  	function getDataAttribute( element, attribute ) {
 279          if ( supportsDataset ) {
 280              return element.dataset[attribute];
 281          }
 282          else {
 283              return element.getAttribute( 'data-' + attribute );
 284          }
 285      }
 286  
 287      /**
 288       * Get element by ID.
 289       *
 290       * Local alias for document.getElementById.
 291       *
 292       * @since 5.1.0
 293       *
 294       * @param {HTMLElement} The requested element.
 295       */
 296  	function getElementById( elementId ) {
 297          return document.getElementById( elementId );
 298      }
 299  
 300      /**
 301       * Moves the reply form from its current position to the reply location.
 302       *
 303       * @since 2.7.0
 304       *
 305       * @memberOf addComment
 306       *
 307       * @param {string} addBelowId HTML ID of element the form follows.
 308       * @param {string} commentId  Database ID of comment being replied to.
 309       * @param {string} respondId  HTML ID of 'respond' element.
 310       * @param {string} postId     Database ID of the post.
 311       * @param {string} replyTo    Form heading content.
 312       */
 313  	function moveForm( addBelowId, commentId, respondId, postId, replyTo ) {
 314          // Get elements based on their IDs.
 315          var addBelowElement = getElementById( addBelowId );
 316          respondElement  = getElementById( respondId );
 317  
 318          // Get the hidden fields.
 319          var parentIdField   = getElementById( config.parentIdFieldId );
 320          var postIdField     = getElementById( config.postIdFieldId );
 321          var element, cssHidden, style;
 322  
 323          var replyHeading         = getElementById( config.commentReplyTitleId );
 324          var replyHeadingTextNode = replyHeading && replyHeading.firstChild;
 325          var replyLinkToParent    = replyHeadingTextNode && replyHeadingTextNode.nextSibling;
 326  
 327          if ( ! addBelowElement || ! respondElement || ! parentIdField ) {
 328              // Missing key elements, fail.
 329              return;
 330          }
 331  
 332          if ( 'undefined' === typeof replyTo ) {
 333              replyTo = replyHeadingTextNode && replyHeadingTextNode.textContent;
 334          }
 335  
 336          addPlaceHolder( respondElement );
 337  
 338          // Set the value of the post.
 339          if ( postId && postIdField ) {
 340              postIdField.value = postId;
 341          }
 342  
 343          parentIdField.value = commentId;
 344  
 345          cancelElement.style.display = '';
 346          addBelowElement.parentNode.insertBefore( respondElement, addBelowElement.nextSibling );
 347  
 348          if ( replyHeadingTextNode && replyHeadingTextNode.nodeType === Node.TEXT_NODE ) {
 349              if ( replyLinkToParent && 'A' === replyLinkToParent.nodeName && replyLinkToParent.id !== config.cancelReplyId ) {
 350                  replyLinkToParent.style.display = 'none';
 351              }
 352  
 353              replyHeadingTextNode.textContent = replyTo;
 354          }
 355  
 356          /*
 357           * This is for backward compatibility with third party commenting systems
 358           * hooking into the event using older techniques.
 359           */
 360          cancelElement.onclick = function() {
 361              return false;
 362          };
 363  
 364          // Focus on the first field in the comment form.
 365          try {
 366              for ( var i = 0; i < commentFormElement.elements.length; i++ ) {
 367                  element = commentFormElement.elements[i];
 368                  cssHidden = false;
 369  
 370                  // Get elements computed style.
 371                  if ( 'getComputedStyle' in window ) {
 372                      // Modern browsers.
 373                      style = window.getComputedStyle( element );
 374                  } else if ( document.documentElement.currentStyle ) {
 375                      // IE 8.
 376                      style = element.currentStyle;
 377                  }
 378  
 379                  /*
 380                   * For display none, do the same thing jQuery does. For visibility,
 381                   * check the element computed style since browsers are already doing
 382                   * the job for us. In fact, the visibility computed style is the actual
 383                   * computed value and already takes into account the element ancestors.
 384                   */
 385                  if ( ( element.offsetWidth <= 0 && element.offsetHeight <= 0 ) || style.visibility === 'hidden' ) {
 386                      cssHidden = true;
 387                  }
 388  
 389                  // Skip form elements that are hidden or disabled.
 390                  if ( 'hidden' === element.type || element.disabled || cssHidden ) {
 391                      continue;
 392                  }
 393  
 394                  element.focus();
 395                  // Stop after the first focusable element.
 396                  break;
 397              }
 398          }
 399          catch(e) {
 400  
 401          }
 402  
 403          /*
 404           * false is returned for backward compatibility with third party commenting systems
 405           * hooking into this function.
 406           */
 407          return false;
 408      }
 409  
 410      /**
 411       * Add placeholder element.
 412       *
 413       * Places a place holder element above the #respond element for
 414       * the form to be returned to if needs be.
 415       *
 416       * @since 2.7.0
 417       *
 418       * @param {HTMLelement} respondElement the #respond element holding comment form.
 419       */
 420  	function addPlaceHolder( respondElement ) {
 421          var temporaryFormId  = config.temporaryFormId;
 422          var temporaryElement = getElementById( temporaryFormId );
 423          var replyElement = getElementById( config.commentReplyTitleId );
 424          var initialHeadingText = replyElement ? replyElement.firstChild.textContent : '';
 425  
 426          if ( temporaryElement ) {
 427              // The element already exists, no need to recreate.
 428              return;
 429          }
 430  
 431          temporaryElement = document.createElement( 'div' );
 432          temporaryElement.id = temporaryFormId;
 433          temporaryElement.style.display = 'none';
 434          temporaryElement.textContent = initialHeadingText;
 435          respondElement.parentNode.insertBefore( temporaryElement, respondElement );
 436      }
 437  
 438      return {
 439          init: init,
 440          moveForm: moveForm
 441      };
 442  })( window );


Generated : Wed Jun 24 08:20:11 2026 Cross-referenced by PHPXref