Ad

Angular: Run Some JQuery Against A Selection Of Views

- 1 answer

I already wrote the code to do this, but the way I did it felt so inelegant I just had to ask if someone had some basic advice for me. Basically I want to run some JQuery to modify the attributes of buttons in a sub-set of views (they're all in the same directory). I wrote the JQuery to do this but I cannot figure out where to put this code.

Right now I'm just shoving a .js file in to those views' directory and using an include statement toward the end of each view file. Not exactly MVC......

Situation

several buttons appear in the interface with no visible label. All the info about them is in a popover attribute. This is fine (or maybe it's bad UI or whatever) as long as you're not blind and using a screen-reader, which won't pick up this popover text. I want to copy the contents of the popover text to an aria-label

adding labels "manually"

Right now the button in question is created within an Angular framework. The Jade for the button looks like this:

button.customize-option(class='Pet_Egg_{{::egg}}',
   ng-class='{selectableInventory: selectedPotion && !(user.items.pets[egg+"-"+selectedPotion.key]>0) && (Content.dropEggs[egg] || !selectedPotion.premium)}',
   popover-title!=env.t("egg", {eggType: "{{::Content.eggs[egg].text()}}"}),
   popover-trigger='mouseenter', popover-placement='right')

(tried to simplify it from the production version) If I wanted to just add an aria-label to this button I could modify it like so:

button.customize-option(class='Pet_Egg_{{::egg}}',
    ng-class='{selectableInventory: selectedPotion && !(user.items.pets[egg+"-"+selectedPotion.key]>0) && (Content.dropEggs[egg] || !selectedPotion.premium)}',
    popover-title!=env.t("egg", {eggType: "{{::Content.eggs[egg].text()}}"}),
    aria-label!=env.t("egg", {eggType: "{{::Content.eggs[egg].text()}}"}),
    popover-trigger='mouseenter', popover-placement='right')

And that works great, buuuuuut there are several dozen other buttons all created with similar but not-identical code. I was adding this aria-label line several dozen times. That felt... inelegant. So I wrote some JQuery!

adding labels "automatically"

$( document ).ready(function() {
  $("button[popover]").each(function(){
    var text = $(this).attr('popover');
    //not everything has a popover-title. If it does we want that title for the aria-label
    if ($(this).attr('popover-title') !== undefined){
      var title = $(this).attr('popover-title');
      $(this).attr('aria-label', title);
    } else {
      //fall back to just using the popover text if no title is present
      $(this).attr('aria-label', text);
    }
  });
  //turns out some of the popovers are on divs which hold the button
  $("div[popover]").each(function(){
    var label = "";
    if ($(this).attr('popover-title') !== undefined){
      label = $(this).attr('popover-title');
    } else {
      label = $(this).attr('popover');
    }
    $(this).find("button").attr('aria-label', label);
  })
});

I was pretty proud of myself! It went from many many lines of code changes across the views to this one short, readable JS file.

Where does that JS go???!?!?

I admit to being an Angular neophyte, but essentially none of the spots I could find made sense for adding this code. The closes I came was a view in shared/ for the footer. I could add the script there and it would run on every route for the app. That's how someone had done the analytics footer already. But there were two problems with this approach:

  1. Not every page/route/view needs this aria-label fix. Most buttons on the site have readable labels. I could add checks to make sure I didn't overwrite an existing aria-label but it would still mean this script was running unnecessarily

  2. I'm not certain that every view has popover text or a popover-title that will work as an aria-label

So, that's where I stand. I'm not certain if a custom controller (?) or something is the best approach to resolve this.

If you want to take a look at the project (without my changes) this is the folder of 'views' where I would be dumping the .js file and adding an include statement to every view.

Ad

Answer

To do the same thing in AngularJS, create a custom directive to add the aria-label attribute to your popovers.

Add Labels Automatically

angular.module('myApp').directive('popover', function () {
    return function(scope,elem,attrs) {
        var text, title, label ;
        if (elem[0].tagName == "BUTTON") {
            text = attrs.popover;
            if (attrs.popoverTitle) {
                title = attrs.popoverTitle;
                attrs.$set('ariaLabel', title);
            } else {
                attrs.$set('ariaLabel', text);
            };
            console.log(elem[0]);
        };
        if (elem[0].tagName == "DIV") {
            label = "";
            if (attrs.popoverTitle) {
                label = attrs.popoverTitle;
            } else {
                label = attrs.popover;
            }
            var button = elem.find("button");
            button.attr('aria-label', label);
            console.log(button[0]);
        };
    };
});

The DEMO on JSFiddle.

Ad
source: stackoverflow.com
Ad