--- a/wp/wp-includes/class-walker-nav-menu.php Fri Sep 05 18:40:08 2025 +0200
+++ b/wp/wp-includes/class-walker-nav-menu.php Fri Sep 05 18:52:52 2025 +0200
@@ -40,6 +40,23 @@
);
/**
+ * The URL to the privacy policy page.
+ *
+ * @since 6.8.0
+ * @var string
+ */
+ private $privacy_policy_url;
+
+ /**
+ * Constructor.
+ *
+ * @since 6.8.0
+ */
+ public function __construct() {
+ $this->privacy_policy_url = get_privacy_policy_url();
+ }
+
+ /**
* Starts the list before the elements are added.
*
* @since 3.0.0
@@ -126,6 +143,7 @@
* @since 4.4.0 The {@see 'nav_menu_item_args'} filter was added.
* @since 5.9.0 Renamed `$item` to `$data_object` and `$id` to `$current_object_id`
* to match parent class for PHP 8 named parameter support.
+ * @since 6.7.0 Removed redundant title attributes.
*
* @see Walker::start_el()
*
@@ -212,17 +230,30 @@
$output .= $indent . '<li' . $li_attributes . '>';
+ /** This filter is documented in wp-includes/post-template.php */
+ $title = apply_filters( 'the_title', $menu_item->title, $menu_item->ID );
+
+ // Save filtered value before filtering again.
+ $the_title_filtered = $title;
+
+ /**
+ * Filters a menu item's title.
+ *
+ * @since 4.4.0
+ *
+ * @param string $title The menu item's title.
+ * @param WP_Post $menu_item The current menu item object.
+ * @param stdClass $args An object of wp_nav_menu() arguments.
+ * @param int $depth Depth of menu item. Used for padding.
+ */
+ $title = apply_filters( 'nav_menu_item_title', $title, $menu_item, $args, $depth );
+
$atts = array();
- $atts['title'] = ! empty( $menu_item->attr_title ) ? $menu_item->attr_title : '';
$atts['target'] = ! empty( $menu_item->target ) ? $menu_item->target : '';
- if ( '_blank' === $menu_item->target && empty( $menu_item->xfn ) ) {
- $atts['rel'] = 'noopener';
- } else {
- $atts['rel'] = $menu_item->xfn;
- }
+ $atts['rel'] = ! empty( $menu_item->xfn ) ? $menu_item->xfn : '';
if ( ! empty( $menu_item->url ) ) {
- if ( get_privacy_policy_url() === $menu_item->url ) {
+ if ( $this->privacy_policy_url === $menu_item->url ) {
$atts['rel'] = empty( $atts['rel'] ) ? 'privacy-policy' : $atts['rel'] . ' privacy-policy';
}
@@ -233,6 +264,17 @@
$atts['aria-current'] = $menu_item->current ? 'page' : '';
+ // Add title attribute only if it does not match the link text (before or after filtering).
+ if ( ! empty( $menu_item->attr_title )
+ && trim( strtolower( $menu_item->attr_title ) ) !== trim( strtolower( $menu_item->title ) )
+ && trim( strtolower( $menu_item->attr_title ) ) !== trim( strtolower( $the_title_filtered ) )
+ && trim( strtolower( $menu_item->attr_title ) ) !== trim( strtolower( $title ) )
+ ) {
+ $atts['title'] = $menu_item->attr_title;
+ } else {
+ $atts['title'] = '';
+ }
+
/**
* Filters the HTML attributes applied to a menu item's anchor element.
*
@@ -255,21 +297,6 @@
$atts = apply_filters( 'nav_menu_link_attributes', $atts, $menu_item, $args, $depth );
$attributes = $this->build_atts( $atts );
- /** This filter is documented in wp-includes/post-template.php */
- $title = apply_filters( 'the_title', $menu_item->title, $menu_item->ID );
-
- /**
- * Filters a menu item's title.
- *
- * @since 4.4.0
- *
- * @param string $title The menu item's title.
- * @param WP_Post $menu_item The current menu item object.
- * @param stdClass $args An object of wp_nav_menu() arguments.
- * @param int $depth Depth of menu item. Used for padding.
- */
- $title = apply_filters( 'nav_menu_item_title', $title, $menu_item, $args, $depth );
-
$item_output = $args->before;
$item_output .= '<a' . $attributes . '>';
$item_output .= $args->link_before . $title . $args->link_after;