I can't see anyone else commenting about this (unless they are calling them something else and I am just missing it) but it would be a cool idea to be able to add custom attributes to a link.

Of course, there is still much debate about this and it may be just wise to have a field where you can "inject" data attribute data.

Anything, just some quick thoughts on this... as usual, this module rules.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

nocean’s picture

I'd also like to see this feature. Seems like this module would be a logical place to include such functionality.

Todd Zebert’s picture

This module provides a hook. I've used it. Do something like:

/**
 * Implements hook_menu_attribute_info().
 */
function MY_MODULE_menu_attribute_info() {
  $info['data-my-attribute'] = array(
    'label' => t('My custom HTML5 attribute'),
    'description' => t('Specifies my custom HTML5 attribute.'),
  );

  return $info;
}

Probably can put that in your template,php too.

*Be sure to edit the above 'data-my-attribute', label and description to fit your needs.*

I've been meaning to contribute an api file for this project.

joelpittet’s picture

@Todd Zebert there is an API file in the dev branch. You are welcome to submit improvements, I'd be happy to review. And thanks for helping with this issue!

knalstaaf’s picture

@Todd Zebert: I installed this module, enabled it, but forgot to alter data-my-attribute.

After applying this to a menu link I can't get rid of it anymore, not even after uninstalling the module, flushing cache etc.

Where can I find this in the database so I can manually remove this attribute again please?

joelpittet’s picture

@knalstaaf unfortunately we are saving all attributes in the serialized options column in the database. And on uninstall we have no way to deal with attributes added by other modules or by you and the ones added by menu_attributes.

My suggestion is look at that column and *VERY carefully*, decided if you are using another module that is storing necessary options. BACKUP your menu_links table, and rewrite that column. Likely good to target a specific menu_name too, to limit any damage.

Todd Zebert’s picture

Just to add some specifics to Joel's comment. It's in the "options" field of the "menu_links" table.

Please take heed of his warnings and precautions.

Depending on your technical skill, a site like http://serialize.onlinephpfunctions.com/ might be helpful in un/serializing the data.

knalstaaf’s picture

Will dive into this, thanks for the push in the right direction!

Anonymous’s picture

May be we can create some ui to add new data-* attribute from config form?

joelpittet’s picture

@bobrov1989 try #2 it creates the UI widgets for whatever data attribute you can imagine.

A UI for adding/removing attribute types would be interesting but I'm wonder if there is enough of a use case?

Anonymous’s picture

@joelpittet the use cases can be different, for example some js libraries and frameworks work with data attributes, it can be used in styling menus etc.

joelpittet’s picture

@bobrov1989 to be clear you are asking for a UI to manage adding and removing these data attributes? As of right now you can add and remove extra attributes including data- attributes with the API as mentioned in #2. So the use-case I'm curious is when people need to do this through the UI?

Anonymous’s picture

ok, it was only proposition)))

joelpittet’s picture

And a good one:) Still wondering how good? :D

Anonymous’s picture

Yes, so do we need to implement this feature?

MickL’s picture

Thumbs up for custom attributes! :)
For example Bootstrap's Modal needs data-toggle and data-target. Or (the tooltip js-library) tipsy needs original-title.

Also it would be very nice to activate/deactivate the attributes in the module-settings. (For example i would like do deactivate releationship, target, access key, but add my custom(bootstrap) attributes.

Anonymous’s picture

Assigned: Unassigned »

Ok, I'll try to take care about it

VVS’s picture

Try this theming.

joelpittet’s picture

Covering everybody's use-case will likely cause feature bloat. The API allows you to extend this module. Maybe worth creating a sandbox for bootstrap_menu_attributes or something to hook in all the needed ones.

Anonymous’s picture

Assigned: » Unassigned
Status: Active » Closed (won't fix)

Yes, you're right, I'll be better.

TheJoker’s picture

@joelpittet I do not understand why you do not want to include these functions in the module. Now the module provides only the basic attributes, but HTML5 is dictating new conditions and a large number of frameworks for a couple of years of use this opportunity.

Thank you for your hard work, but I think it is unnecessary conservatism. Please start to move towards professional users, since this module meets the needs of beginners only. For more professional users constantly have to create hooks and climb in the code, rather than save time. Although the modules are designed for our convenience, the rejection of this attribute is at odds logic.

TheJoker’s picture

Status: Closed (won't fix) » Needs review
joelpittet’s picture

Status: Needs review » Closed (won't fix)

@TheJoker you could make another contrib or sandbox module that extends menu_attributes that does the logic for custom data attributes. If it gets the usage you expect as being needed by a wide variety of people maybe we can merge it into menu_attributes.

I'll add any hooks you need to make it work well with menu_attributes and if it works I'll even link to it from the project page.

There is nothing to review here as there is no patch, if you want to re-open set it to 'active'.

katannshaw’s picture

@Joelpittet (and any others): Related to the original post and replies, I'm attempting to use the API to add custom menu attributes with the following code in my theme's template.php file:

/**
 * Implementation of hook_menu_attribute_info().
 */
function MYTHEME_menu_attribute_info() {
  // Add a 'role' attribute.
  $info['role'] = array(
    'label' => t('Role'),
    'description' => t('Specifies the role attribute for the link.'),
    'form' => array(
      '#maxlength' => 15,
      '#size' => 10,
    ),
    'scope' => array(MENU_ATTRIBUTES_LINK, MENU_ATTRIBUTES_ITEM),
  );
  // Add a 'aria-haspopup' attribute.
  $info['aria-haspopup'] = array(
    'label' => t('Tabindex'),
    'description' => t('Specifies the aria-haspopup attribute for the link.'),
    'form' => array(
      '#maxlength' => 5,
      '#size' => 5,
    ),
    'scope' => array(MENU_ATTRIBUTES_LINK, MENU_ATTRIBUTES_ITEM),
  );
  // Add a 'aria-expanded' attribute.
  $info['aria-expanded'] = array(
    'label' => t('Tabindex'),
    'description' => t('Specifies the aria-expanded attribute for the link.'),
    'form' => array(
      '#maxlength' => 5,
      '#size' => 5,
    ),
    'scope' => array(MENU_ATTRIBUTES_LINK, MENU_ATTRIBUTES_ITEM),
  );
  // Add a 'tabindex' attribute.
  $info['tabindex'] = array(
    'label' => t('Tabindex'),
    'description' => t('Specifies the tab order for the link.'),
    'form' => array(
      '#maxlength' => 3,
      '#size' => 2,
    ),
    'scope' => array(MENU_ATTRIBUTES_LINK),
  );
  return $info;
}

Even after clearing my cache, these fields are not showing up under my site's main menu as seen in the attached screenshot. Is there a step that I'm missing to get these fields added? Thanks for any assistance.

joelpittet’s picture

That must be in a module because those hooks are using module_invoke_all()

katannshaw’s picture

@Joelpittet: Thank you for the prompt answer. I will make that change.

UPDATE: @Joelpittet: I created a separate module with the following two files and these new custom menu attributes are still not showing. This module is showing in the list of modules and I did enable it and clear the cache. Do you see a step that I'm missing?

MYMODULE_menu_attributes.info

name = MYMODULE Menu attributes
description = "Allows administrators to specify custom attributes for MYMODULE menu items."
dependencies[] = menu
files[] = MYMODULE_menu_attributes.module
core = 7.x
package = "Menu Attributes"
configure = admin/structure/menu/settings

MYMODULE_menu_attributes.module

<?php
/**
 * Implementation of hook_menu_attribute_info().
 */
function MYMODULE_menu_attribute_info() {
  // Add a 'role' attribute.
  $info['role'] = array(
    'label' => t('Role'),
    'description' => t('Specifies the role attribute for the link.'),
    'form' => array(
      '#maxlength' => 15,
      '#size' => 10,
    ),
    'scope' => array(MENU_ATTRIBUTES_LINK, MENU_ATTRIBUTES_ITEM),
  );
  // Add a 'aria-haspopup' attribute.
  $info['aria-haspopup'] = array(
    'label' => t('Tabindex'),
    'description' => t('Specifies the aria-haspopup attribute for the link.'),
    'form' => array(
      '#maxlength' => 5,
      '#size' => 5,
    ),
    'scope' => array(MENU_ATTRIBUTES_LINK, MENU_ATTRIBUTES_ITEM),
  );
  // Add a 'aria-expanded' attribute.
  $info['aria-expanded'] = array(
    'label' => t('Tabindex'),
    'description' => t('Specifies the aria-expanded attribute for the link.'),
    'form' => array(
      '#maxlength' => 5,
      '#size' => 5,
    ),
    'scope' => array(MENU_ATTRIBUTES_LINK, MENU_ATTRIBUTES_ITEM),
  );
  // Add a 'tabindex' attribute.
  $info['tabindex'] = array(
    'label' => t('Tabindex'),
    'description' => t('Specifies the tab order for the link.'),
    'form' => array(
      '#maxlength' => 3,
      '#size' => 2,
    ),
    'scope' => array(MENU_ATTRIBUTES_LINK),
  );
  return $info;
}
katannshaw’s picture

@joelpittet: I was hoping for an update. Do you have any suggestions?

joelpittet’s picture

FileSize
90.28 KB

You don't need files[] = MYMODULE_menu_attributes.module and probably should have
dependencies[] = menu_attributes in the info file.

Those shouldn't change how it works, I copied your code in and it worked as expected. Here's a screen shot. Everything is named Tabindex but that's just a copy change.

katannshaw’s picture

@joelpittet: That was it. Thanks for your help on this! I very much appreciate it.

oomphinc’s picture

@jayhawkfan75 @joelpittet

Any reason NOT to contribute these options back to the core module?
Im assuming that @jayhawkfan75 was adding these options for accessibility standards.

joelpittet’s picture

Their usage is rare, you could consider creating a menu_attributes_a11y contrib module if you use them enough to warrant it. If that gets the uptake then maybe we can consider merging them later. I'd even be willing to promote it on the project page.

katannshaw’s picture

@joelpittet: I would love to contribute back a menu_attributes_a11y contrib module to the project once I get my own issue resolved.

I'm able to get back to work on this custom module and for some reason these custom attributes are not getting added to the admin/structure/menu/item/#/edit form. Here's the code I'm using, and I've checked that this custom module is enabled. It looks the same as what was in a previous post that *was* working so I'm stumped. Hopefully this is something obvious. Please help!

Kat

MYTHEME_menu_attributes.info

name = MYTHEME Menu attributes
description = "Allows administrators to specify custom attributes for MYTHEME menu items."
dependencies[] = menu
dependencies[] = menu_attributes
core = 7.x
package = "Menu Attributes"
configure = admin/structure/menu/settings

MYTHEME_menu_attributes.module

<?php
/**
 * Implementation of hook_menu_attribute_info().
 *
 * Inform the menu_attributes module about custom attributes.
 *
 * @return
 *   An array of attributes to be controlled by Menu Attributes, keyed by
 *   attribute name. Each attribute record should be an array with the following
 *   key/value pairs:
 *   - label: The human-readable name of the attribute.
 *   - description: The attribute description.
 *   - form: A Form API array. Some default values for this array are provided
 *     in menu_attributes_get_menu_attribute_info().
 *   - scope: An array of scope options, MENU_ATTRIBUTES_LINK or
 *     MENU_ATTRIBUTES_ITEM or both. If no scope is provided, both will
 *     be assumed.
 *
 * @see menu_attributes_menu_attribute_info()
 * @see menu_attributes_get_menu_attribute_info()
 */
function THEMENAME_menu_attribute_info() {
  // Add a 'role' attribute.
  $info['role'] = array(
    'label' => t('Role'),
    'description' => t('Specifies the role attribute for the link.'),
    'form' => array(
      '#maxlength' => 15,
      '#size' => 10,
    ),
    'scope' => array(MENU_ATTRIBUTES_LINK, MENU_ATTRIBUTES_ITEM),
  );
  // Add a 'aria-haspopup' attribute.
  $info['aria-haspopup'] = array(
    'label' => t('aria-haspopup'),
    'description' => t('Specifies the aria-haspopup attribute for the link.'),
    'form' => array(
      '#maxlength' => 5,
      '#size' => 5,
    ),
    'scope' => array(MENU_ATTRIBUTES_LINK, MENU_ATTRIBUTES_ITEM),
  );
  // Add a 'aria-expanded' attribute.
  $info['aria-expanded'] = array(
    'label' => t('aria-expanded'),
    'description' => t('Specifies the aria-expanded attribute for the link.'),
    'form' => array(
      '#maxlength' => 5,
      '#size' => 5,
    ),
    'scope' => array(MENU_ATTRIBUTES_LINK, MENU_ATTRIBUTES_ITEM),
  );
  // Add a 'tabindex' attribute.
  $info['tabindex'] = array(
    'label' => t('Tabindex'),
    'description' => t('Specifies the tab order for the link.'),
    'form' => array(
      '#maxlength' => 3,
      '#size' => 2,
    ),
    'scope' => array(MENU_ATTRIBUTES_LINK),
  );
  return $info;
}
joelpittet’s picture

My first guess would be that it says THEMENAME but it should be a module not a theme, but it's in a .module file that should be just accidental?

Otherwise it looks correct.

katannshaw’s picture

I just replaced the beginning of the name with THEMENAME. It's a habit since I mostly work with themes. It should be MODULENAME. That's the only difference. The module is placed at sites/all/modules/contrib/.

Should I add this function to the end of my custom code from the API?

/**
 * Fetch an array of menu attributes.
 */
function menu_attributes_get_menu_attribute_info() {
  $attributes = module_invoke_all('menu_attribute_info');

  // Merge in default values.
  foreach ($attributes as $attribute => &$info) {
    $info += array(
      'form' => array(),
      'enabled' => variable_get("menu_attributes_{$attribute}_enable", 1),
      'default' => '',
    );
    $info['form'] += array(
      '#type' => 'textfield',
      '#title' => $info['label'],
      '#description' => isset($info['description']) ? $info['description'] : '',
      '#default_value' => variable_get("menu_attributes_{$attribute}_default", $info['default']),
    );
  }
  drupal_alter('menu_attribute_info', $attributes);

  return $attributes;
}

Any other suggestions?

Kat

joelpittet’s picture

Oh definitely not, that code is in menu_attributes and would cause a fatal error of duplicate function name, also that is the code that should be calling your code.

I'd suggest creating a sandbox for this, put the code you have so far up in there and put a link here and I'll try it out.

katannshaw’s picture

@joelpittet: I've got the sandbox initially set up at https://www.drupal.org/sandbox/katannshaw/2923855

Do I need to just upload the 2 module files to the sandbox or do I need to first pull in D7 and then add the module to it before pushing that up to the sandbox? This is my first time creating a sandbox :-)

Kat

joelpittet’s picture

Just the 2 files. Cool, first sandbox!

katannshaw’s picture

Thanks! There's a first time for everything. Here you go: https://www.drupal.org/node/2923855/commits

katannshaw’s picture

@joelpittet: Have you had a chance to look at my sandbox project yet? I really need to get this issue resolved, and it would also be great to have this contribution committed if it's good.

bmango’s picture

@jayhawkfan75 - I used your code to create a data-target attribute (used for scrollspy in Bootstrap) in a custom module and it worked great. Thank you.

joelpittet’s picture

The sandbox looks great and sounds like @bmango has tested and works for him.

I'm not sure what CMoG is, but maybe you can make it a real project and change it to menu_attributes_a11y or something generic?

katannshaw’s picture

@bmango: That's great to hear! Thanks so much for letting me know. I'm glad that it was helpful!

@joelpittet: Absolutely! I hadn't realized that I had committed the cmog files instead of the more generically-named files. I'll make that change and add an update to this ticket when that's done. I'll need to create a project page as well correct? Can you forward me to where I would do that? I'm a newbie to all of this :-)

Kat

joelpittet’s picture

I've never done the sandbox to full project promotion myself but here's the docs I found: https://www.drupal.org/node/1068952

katannshaw’s picture

@joelpittet: Thanks for those docs. I haven't had time to get to that yet but I have something that needs to be addressed beforehand.

Even after my custom module is enabled, my local version of this code is still not getting rendered on the /admin/structure/menu/item/#/edit page under the "Menu link attributes" and "Menu item attributes" accordion sections. This is the code that I'm presently using based off of the original API:

cmog_menu_attributes.info

name = CMoG Menu attributes
description = "Allows administrators to specify custom attributes for CMoG menu items."
dependencies[] = menu
dependencies[] = menu_attributes
version = "7.x-1.0"
core = 7.x
package = "CMoG"
configure = admin/structure/menu/settings

cmog_menu_attributes.module

<?php
/**
 * Implementation of hook_menu_attribute_info().
 *
 * Inform the menu_attributes module about custom attributes.
 *
 * @return
 *   An array of attributes to be controlled by Menu Attributes, keyed by
 *   attribute name. Each attribute record should be an array with the following
 *   key/value pairs:
 *   - label: The human-readable name of the attribute.
 *   - description: The attribute description.
 *   - form: A Form API array. Some default values for this array are provided
 *     in menu_attributes_get_menu_attribute_info().
 *   - scope: An array of scope options, MENU_ATTRIBUTES_LINK or
 *     MENU_ATTRIBUTES_ITEM or both. If no scope is provided, both will
 *     be assumed.
 *
 * @see menu_attributes_menu_attribute_info()
 * @see menu_attributes_get_menu_attribute_info()
 */
function cmog_menu_attribute_info() {
  // SAMPLE.
  // $info['accesskey'] = array(
  //   'label' => t('Access Key'),
  //   'description' => t('Specifies a <a href="@accesskey">keyboard shortcut</a> to access this link.', array('@accesskey' => url('http://en.wikipedia.org/wiki/Access_keys'))),
  //   'form' => array(
  //     '#maxlength' => 1,
  //     '#size' => 1,
  //   ),
  //   'scope' => array(MENU_ATTRIBUTES_LINK),
  // );

  // Add a 'role' attribute.
  $info['role'] = array(
    'label' => t('Role'),
    'description' => t('Specifies the role attribute for the link.'),
    'form' => array(
      '#maxlength' => 15,
      '#size' => 10,
    ),
    'scope' => array(MENU_ATTRIBUTES_LINK, MENU_ATTRIBUTES_ITEM),
  );
  // Add a 'aria-haspopup' attribute.
  $info['aria-haspopup'] = array(
    'label' => t('aria-haspopup'),
    'description' => t('Specifies the aria-haspopup attribute for the link.'),
    'form' => array(
      '#maxlength' => 5,
      '#size' => 5,
    ),
    'scope' => array(MENU_ATTRIBUTES_LINK, MENU_ATTRIBUTES_ITEM),
  );
  // Add a 'aria-expanded' attribute.
  $info['aria-expanded'] = array(
    'label' => t('aria-expanded'),
    'description' => t('Specifies the aria-expanded attribute for the link.'),
    'form' => array(
      '#maxlength' => 5,
      '#size' => 5,
    ),
    'scope' => array(MENU_ATTRIBUTES_LINK, MENU_ATTRIBUTES_ITEM),
  );
  // Add a 'tabindex' attribute.
  $info['tabindex'] = array(
    'label' => t('Tabindex'),
    'description' => t('Specifies the tab order for the link.'),
    'form' => array(
      '#maxlength' => 3,
      '#size' => 2,
    ),
    'scope' => array(MENU_ATTRIBUTES_LINK),
  );
  return $info;
}

/**
 * Alter the list of menu item attributes.
 *
 * @param $attributes
 *   An array of attributes to be controlled by Menu Attributes, keyed by
 *   attribute name.
 *
 * @see hook_menu_attribute_info()
 * @see menu_attributes_get_menu_attribute_info()
 */
function cmog_menu_attribute_info_alter(array &$attributes) {
  // Remove the Access Key attribute.
  unset($attributes['accesskey']);
}

Here is how it gets rendered on my local site:
Screenshot

I've attached the 2 module files for you or others to test if you can. Do you have any suggestions on what I'm missing?

Kat

katannshaw’s picture

@joelpittet: Have you had a chance to look into this for me?

Kat