A very well known fact to improve the performance of a Flex app is to reduce the amount of nested containers, yet doing so seems particularly hard for me to do.
I'm developing a mobile app and resizing some of my components at runtime is quite slow (I can switch components to fullscreen and back again), we're speaking of 500-1000ms, which isn't really nice. Less complex components resize instantly with no noticeable lag, which is what I'd like for all components.
Let's assume the following component (simplified of course and some groups are components by themselves, but the nesting level is quite accurate);
<VGroup "the component's base">
// I guess this is fine
<HGroup "the component's title bar">
<SkinnableContainer "title bar skin">
<title bar components, labels etc. />
</SkinnableContainer>
</HGroup>
<HGroup "options bar that has switchable components">
<button />
<array "holds 'views' for the options bar that can be switched">
<HGroup "one option view">
<option view contents, labels etc. />
</HGroup>
<HGroup "another option view">
<option view contents, labels etc. />
</HGroup>
</array>
<button />
</HGroup>
That's it for the basic component layout. I'm not sure if the options bar can be optimised, the two buttons are used to switch the content which itself is placed between the buttons, hence the upper HGroup. The components inside the array need to be aligned horizontally as well, hence the child HGroups. That's already down to nesting level 3 in this component, which by itself is already a level 3 container (due to my navigation).
To the component's content area;
<Group "this is the content area">
// this group needs two different layouts (vertical and horizontal) that
// are switched based upon the user's choice of having the component maximised
// or minimised
<layouts />
// this list changes it's size based on the selected layout
<List />
this group also changes it's size based on the layout
<VGroup>
<scroller>
// the group here holds a large label that needs to be scrollable
<group>
</scroller>
<HGroup>
<some basic components like `SkinnableContainer` and `Label` />
</HGroup>
</VGroup>
</Group>
And that's pretty much the layout of my worst performing (layout resizing wise) component, closing tag...
</VGroup>
... and we're done.
So the big question, is there room to optimise this? If so, where can I start? Obviously the layout manager has to calculate quite much during the layout switching process.
Unfortunately, since this is a mobile app, I can't work with absolute sizes at all as I need this app to work on a variety of platforms and resolutions, thus all groups have relative sizes assigned, usually 100% for both width and height.
I'd really like to streamline this, but I just don't know where to start. Any tips what I could do?
I was in charge of much of the same thing for a previous project, and while there was no 1 magic bullet that eliminated a chunk of problems, it fell in line with the "death by a thousand papercuts" theory.
A few examples of improvement based on yours:
The TitleBar: The skinnable container also has a layout property, why not just eliminate the outer Group shell, and start with the SC as the base?
<!-- <HGroup "the component's title bar"> -->
<SkinnableContainer id="originalGroupId" "title bar skin">
<layout>
<HorizontalLayout />
<layout>
<title bar components, labels etc. />
</SkinnableContainer>
<!-- </HGroup> -->
The OptionsBar: I don't know if there are limitations in the mobile space, but can you use a container that uses the INavigatorContent interface? IE. Something that will essentially use a creationPolicy flag that will only create the grandchildren of the container until the user actually requests it. With groups there is no concept of virtualization so all components get created at the instantiation of the parent.
<ViewStack "options bar that has switchable components">
<button />
</ViewStack>
The Component Area: This gets a little more challenging, sometimes it helps (at least it does for me) to take a 10,000ft view of what information you really want to display. For instance, the scrolling for a really long label, can you use something like a TextAera instead? If so that would eliminate the outer shell:
// the group here holds a large label that needs to be scrollable
<TextArea text="my really large label">
</TextArea>
Hope that helps...
Might be irrelevant, but I'd also advise against using Lists. Maybe it's not the same, but I changed one of my components from using List to dynamic skin parts and rendering of said component was noticeably faster (600 vs 100ms). Lists are pretty heavy in Spark.
Related
Context:
We have some cards that are radio buttons in a radio group, that we want to display in a different order for small and large breakpoints. I want to know if there is a way to do this nicely that doesn't involve duplicating the html for the different breakpoints, and doesn't break accessibility.
Desired large breakpoint:
|Large|Medium|Small|
Desired small breakpoint:
|Small|
|Medium|
|Large|
Desired accessibility behaviour:
With a radio group, you'd normally expect to hit tab to get to the first option in the radio group, and then use the arrow keys to select different options.
What I've tried:
I've tried using display:flex and changing flex-direction to either row or column-reverse depending on the breakpoint. And then because flex is visual only, I have also put tabindex onto the radio inputs. Although it fixes the tab behaviour, the arrow keys are the reverse of what you'd expect.
Super cut down example:
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<p>Standard view:</p>
<div style="display:flex;">
<div>
<input type="radio" id="featured" name="tier2" value="featured" tabindex="1">
<label for="featured">Featured</label><br>
</div>
<div>
<input type="radio" id="standard" name="tier2" value="standard" tabindex="2">
<label for="standard">Standard</label><br>
</div>
<div>
<input type="radio" id="basic" name="tier2" value="basic" tabindex="3">
<label for="basic">Basic</label>
</div>
</div>
<p>Mobile view:</p>
<div style="display:flex; flex-direction:column-reverse">
<div>
<input type="radio" id="featured" name="tier" value="featured" tabindex="3">
<label for="featured">Featured</label><br>
</div>
<div>
<input type="radio" id="standard" name="tier" value="standard" tabindex="2">
<label for="standard">Standard</label><br>
</div>
<div>
<input type="radio" id="basic" name="tier" value="basic" tabindex="1">
<label for="basic">Basic</label>
</div>
</div>
</body>
</html>
Questions:
Is there another attribute like tabindex that you can use to define the order the arrow keys work?
Is there any other way of doing this that doesn't involve duplicating html?
Thanks!
Short Answer
Don't use tabindex, flex etc.
Instead serve the mobile version of the HTML and reorder the DOM order of your component using JavaScript on page load for larger screens.
Longer Answer
Is there another attribute like tabindex that you can use to define the order the arrow keys work?
No, sadly there isn't.
Also using tabindex is a terrible idea as you will break logical tab order for the rest of your application. Avoid it at (nearly) all costs!
Is there any other way of doing this that doesn't involve duplicating html?
There is no clean way to do it without duplicating the HTML (that will work with all screen reader and browser combinations).
You do have a few options though
Reorder the HTML on the server - You could load the component via AJAX after querying the screen width. Decide which version to send based on the screen size (send the screen size to the server with the AJAX request).
Reorder the HTML on page load - I would start with your mobile layout as the base HTML (to minimise JS work on mobiles as they are less powerful). Then if the screen size is over your break point rearrange the items using JS on page load for larger screens.
The key to both is that DOM order is correct. It will save you a lot of headaches if you can somehow get your DOM order as you want it rather than trying to change it with CSS, tabindex etc.
Both options do the same thing but both have pros and cons:-
Option 1 - Reorder the HTML on the server
Pro - the page will load faster and lazy loading the item in has performance benefits.
Pro - your HTML is clean and exactly what is required, it even gives the option for different lists entirely if you desire.
Pro - probably easier to maintain as you just have a raw HTML file to edit on the server (albeit 2 files, one for each view) and don't need to do mental gymnastics to work out reordering if the list grows longer.
Con - when you JavaScript fails the item will not load in at all
Con - an extra network call may actually end up slower, you would have to test. Yes this conflicts with the first "Pro" so you would have to test it!
Option 2 - Reorder the HTML on page load
Pro - By going mobile first it will not add anything other than maybe a KB of JavaScript to the page needed to reorder on desktop.
Pro - when your JavaScript fails the item will still render, it just might not be in the order you desire.
Con - As your list grows it may be harder to maintain the reordering.
Con - Despite the earlier "Pro" about JavaScript size, it is also a "con", you are still sending extra info down the wire and have to do a screen size check. Minor points but if performance is important something to consider.
What would I do?
If I had to do this I would probably go for option 2.
The main reason being that the tiny amount of JS is nothing compared to a full AJAX call and performance is key.
This is assuming the form is not "above the fold", in which case I would go for option 1 to avoid Cumulative Layout Shift, as that would annoy users seeing the list reorder while the page loads.
I am trying to implement the notification component that will show the list of the items and will be opened by clicking on the notification icon on the fixed navigation bar on the top. I don't think it's a menu bar. Because the menu provides the actions that can be performed and it can also have a sub-menu.
https://www.w3.org/TR/wai-aria-practices/#menu
Can anyone let me know what should be the aria-role of such kind of components?
Below is the code sample. I will open the template dynamically by clicking on the notification icon button:-
<button aria-label="notifications">
<mat-icon class="mr-md">notifications</mat-icon>
</button>
<!-- Notification template -->
<div class="notifications__item">
Notifications
<li *ngFor="let notification of notifications" class="notifications__item">
<mat-icon class="notifications__icon material-icons-round">
{{ notification.icon }}
</mat-icon>
<div class="notifications__content">
<div [ngClass]="{ 'notifications__warn': notification?.type }">
<span>{{ notification.title }}</span>
</div>
<div>{{ notification.description }}</div>
</div>
<small class="notifications__caption">
{{ notification.duration }}
</small>
</li>
</div>
There are still a lot of things to consider that your example doesn't cover, so this isn't a complete answer, it is just pointing you to the relevant WAI-ARIA depending on what route you take.
The button
The first thing to consider is the button. You need to tell screen reader users what state it is currently in. For this we use aria-expanded to indicate whether the item it controls is currently opened or closed. (aria-expanded="true" for open, aria-expanded="false" for closed.)
At the same time we want to indicate what item this button controls (as the notification list isn't 'owned' by the element - for example if it was an <li> with a nested <ul> in a menu then the list would be 'owned' by it).
For this we would use aria-controls or aria-owns and point it to the ID of the element it controls. For the difference between them see this stack overflow post as a good explanation, in this example I would say it is aria-controls but yet again depends on your implementation and positioning in the DOM.
With regards to the button itself and where it sits in your menu, this is still considered navigation so it should sit within your <nav> element. However if this sits outside of your navigation along with say a 'help' and 'account' section you may consider those items part of a toolbar. (yet again I would say it doesn't apply here but something to look at)
Also it doesn't appear to be applicable here but if you include any links etc. within the 'popup' / modal that shows the notification list (i.e. a 'view all notifications' link), you should consider aria-haspopup="true"
The notification list
Right so we have a button pointed to the container (don't forget to give the container the relevant ID for aria-owns or aria-controls). Next what about the container itself?
Well in this example it appears that the container should be treated like a modal.
So for this reason you need to consider:-
trapping focus in the modal,
close with Escape,
returning focus to the button that activated it on close,
providing a close button that is accessible by keyboard,
a title for the modal (even if it is visually hidden)
What I would recommend is add some of the accessibility features above, try it with a screen reader and keyboard and see if it is easy to use. Once you have decided on your pattern ask some more questions on specific use case issues as the above is general guidance.
A few things to consider based on your markup
Additional things to consider from your example:-
use aria-hidden="true" on your icons, they don't add anything for screen readers (assuming your notification.title is descriptive).
For the notification title consider making it a relevant heading (<h2> - <h6> depending on position in document.
Don't forget to add some visually-hidden text that describes the warning level (I can see you have some form of colouring / distinction in [ngClass]="{ 'notifications__warn': notification?.type }" - expose the same info to screen readers.)
You currently have a <li> within a <div> - maybe change the outer <div> into an <ul> so it is semantically correct (<div class="notifications__item"> into <ul class="notifications__item">)
I hope the above is useful to set you on the right track, a lot to read but after reading the linked articles you should be able to make a better decision on what pattern you are using (as I didn't even mention making this a sub item within your menu) and can then ask some more questions on specific details you don't yet understand.
final thoughts / tips
test with a screen reader - this is the biggest tip I can give on working out how WAI-ARIA works and interacts with things.
Also if you are ever in doubt as to whether a WAI-ARIA attributre is applicable it is better to not include it.
Incorrect use or WAI-ARIA is actually worse than not including it at all so make sure you understand when to use an attribute reasonably well before implementing it. If I am ever unsure (as it still happens to me!) I tend to look at 2 or 3 examples of it in use and see if my pattern fits the examples I looked at.
I'm developing an SAP-ui5 XML view. In that view I created dynamic custom tiles using the CustomTile tag, as demonstrated in this image:
<TileContainer
id="container"
height="400px"
tileDelete="handleTileDelete"
tiles="{/TileCollection}">
<tiles>
<CustomTile id="ct1">
<content>
<VBox>
<Toolbar class="backcolor" design="Transparent">
<Text class="sapMHeader" text="Dynamic content" />
</Toolbar>
<Button type="{infoState}" text="Button"
icon="sap-icon://approvals"
ariaDescribedBy="defaultButtonDescription genericButtonDescription">
</Button>
</VBox>
</content>
</CustomTile>
</tiles>
</TileContainer>
Currently, clicking on the navigate button slides three tiles per click. I want to get it to slide only one tile per click. How do I achieve this?
The TileContainer control is responsive meaning how many tiles you see totally depends on the available screen space and nothing else. The scroll-buttons act as an overflow and slide a complete 'page' just as a Carousel would do. There is no way of changing this behavior via existing API.
If you really want to adapt this behavior TileContainer.prototype.scrollLeft and TileContainer.prototype.scrollRight are probably a good starting point.
BR
Chris
To be honest, I am just waiting for Günter to answer my question (lol) ^_^
Anyhow, I am writing on a project which starts with a pretty huge list of items (1500+). Some of them are used for a selection mask inside DropDowns and about 800 of them are displayed in a grid.
I tried to do the best I can to keep everything fast and smooth but loading the website takes about 20-30 seconds which is a little bit too long.
To address this issue I wanted to make use of <core-list> which allows me to render only about 8 items per DropDown instead of all of them. This would reduce the amount of items rendered for DropDowns from 700 to 32. However, I HAVE to use some observables in the DropDowns and since there is a bug accessing those I cannot make use of <core-list>.
My HTML Code for DropDowns:
<div relative flex>
<paper-input-decorator label="Committee" on-click="{{openDropdown}}" dropdown="{{committeeDropdownId}}">
<input is="core-input" value="{{committee}}">
</paper-input-decorator>
<paper-shadow absolute z="2" class="dropdown" hidden?="{{!openedState[committeeDropdownId] || committees.length < 2}}" style="width: 100%">
<div id="{{committeeDropdownId}}" style="width: 100%">
<template repeat="{{item in committees}}">
<paper-item
hidden?="{{!activeCommittees[item['v']] || !containsText(item['k'], committee)}}"
on-click="{{onItemSelected}}"
value="{{item['v']}}">{{item['k']}}</paper-item>
</template>
</div>
</paper-shadow>
</div>
As you can see, I have to access containsText(..) as well as activeCommittees inside the template which would be the template I have to use for <core-list>. With that in mind I added containsText(..) to my item class which I would use for <core-list> and added observable getters for committee and activeCommittees. Since these getters have to access the 'original' variables I declared these variables static and pretty much everything was dirty and ugly and didn't really work any longer.
First Question: So the first part of my question is, can I tweak the performance of the creation of my DropDowns WITHOUT <core-list>?
But there is more. There is a grid containing about 800 little 20x20 pixel cells. Constructing this also takes a while.
The grid HTML code looks like this:
<div vertical layout>
<template repeat="{{row in rows}}">
<div row horizontal layout>
<template repeat="{{col in row.items}}">
<template if="{{!col.dummy}}" bind="{{col.delegate as item}}">
<core-tooltip disabled?="{{!activeMEPs[item['v']]}}">
<div mep="{{item['v']}}" class="grid-item item"
low?="{{mepFactors[item['v']][0] <= 0.1}}"
style="opacity: {{mepFactors[item['v']][0]}}; background: {{color(item['v'], activeMEPs[item['v']])}};">
</div>
<div tip>
<!-- Name in Tooltip -->
<h4 style="margin: 0em 0 0.1em;">{{Database[item['v']]['k']}}</h4>
</div>
</core-tooltip>
</template>
<template if="{{col.dummy}}">
<div class="grid-item dummy"></div>
</template>
</template>
</div>
</template>
</div>
This may look more complicated than it is, but I create N rows mit M columns and fill the remaining columns with empty dummy container.
Second Question: So here comes the second part of my question, how can I increase the performance of creating my grid?
It's all about performance, right? Thanks for reading the longest post I ever made!
What core-list does to improve performance, is to only render to the DOM what is visible to the user. Everything outside the current view is only rendered when it is scrolled into view. This is usually not trivial work.
Currently development of core-/paper-elements seems a bit stale because of the transition to Polymer 0.8. I doesn't make sense to put too much effort in the 0.5 versions of the elements when a lot of work has to be redone when 0.8 is in a state that more advanced elements can be ported.
Instead of core-list you could try bwu_datagrid which also has a lot of such optimizations. BWU Datagrid also doesn't use Polymer data binding which is known to be not the fastest possible way to show data on the page. BWU Datagrid also has some issues. For example, it doesn't work well on other browsers than Chrome yet, because I also don't want to put too much effort into it before 0.8 is ready.
If you don't plan to release your application within the next weeks it might make sense to pub such performance optimizations on hold until you get them for free but it is possible that it takes some months until it becomes available.
I'm trying to recreate a similar look to the Windows 8 app view and I'm having an issue with the display. If your running Windows 8 and you're on the home page, just click the down arrow icon located at the bottom of the screen. This takes you to a list of all of the applications installed on your computer grouped alphabetically. This is essentially what I'm trying to recreate visually (only my data is not apps).
What I want:
I've created a grouped ListView (alphabetical) using the html/css/js method and what I get is close, but not exact. I get "A" followed by items that start with A.... then a new column "B" followed by B items... new column "C", etc. If there are only 2 "A" items I want "B" to show up next in the list. Not in a new column. Basically, I want to override the block formatting of the display but I can't seem to figure out how. I've played around with the DOM Explorer and there are so many classes and styles I haven't been able to figure out what needs to be changed.
What my app looks like:
Has anyone else done this? Does anyone have any suggestions I can try? Thanks.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Grocery List</title>
<link href="grocerylist.css" rel="stylesheet" />
<script src="/js/groceryData.js"></script>
<script src="grocerylist.js"></script>
</head>
<body>
<div class="grocerylist fragment">
<header class="page-header" aria-label="Header content" role="banner">
<button class="back-button" data-win-control="WinJS.UI.BackButton"></button>
<h1 class="titlearea win-type-ellipsis">
<span class="pagetitle">Grocery List</span>
</h1>
</header>
<section class="page-section" aria-label="Main content" role="main">
<!-- Template for the grouped headers for the list -->
<div id="headerTemplate" data-win-control="WinJS.Binding.Template" style="display: none">
<div class="simpleHeaderItem">
<h1 data-win-bind="innerText: title"></h1>
</div>
</div>
<!-- Template for each item in the list -->
<div id="mediumListIconTextTemplate" data-win-control="WinJS.Binding.Template" style="display: none">
<div id="groceryNameTemplate">
<!-- Display the name of our food -->
<h4 data-win-bind="innerText: name"></h4>
</div>
</div>
<!-- Our list of available grocery items -->
<div id="groceryListView"
data-win-control="WinJS.UI.ListView"
data-win-options="{ itemDataSource : GroceryData.itemList.dataSource,
itemTemplate: select('#mediumListIconTextTemplate'),
groupDataSource: GroceryData.itemList.groups.dataSource,
groupHeaderTemplate: select('#headerTemplate'),
layout: { type : WinJS.UI.GridLayout },
itemsDraggable: true,
itemsReorderable: false }">
</div>
</section>
</div>
</body>
</html>
Code added as requested. I went through all of my CSS and 100% of it is style only (colors, fonts, etc) all placement and positioning is defined in the WinJS styles which come with ListView.
In essence, the Apps view is just a standard 2D grid layout in a ListView, laid out in vertical columns first, then horizontally.
The trick is that there are two different item types: a letter/header item and an app item. This means that your data source--which in this case can be a single WinJS.Binding.List, is a single list of the apps sorted alphabetically, with then header header/letter items inserted at the appropriate points before items that start with that letter. (Remember to use globalization-sensitive collation if you're sorting alphabetically.)
With such a list, however, you cannot rely on declarative templates for your rendering, because you need to render both types of items distinctly. For this you need to use an item rendering function instead of a template. Because the rendering function gets called for each item, it can check the type and dynamically render each one according to its data. In the Apps view, an App item would be rendered with an icon and name, whereas a header/letter would be rendered with just text. The item renderer also assigns appropriate behaviors, which in the Apps view is that app items launch the app and header items do a semantic zoom out.
A rendering function is really the approach to take whenever you have a standard layout--a GridLayout again in this case--but need per-item rendering control. (This is different from a custom layout that lets you depart from the non-standard layout algorithms, and of course a custom layout can also be combined with a rendering function.)
For all the details of rendering functions, check out chapter 7 of my free ebook, Programming Windows Store Apps with HTML, CSS, and JavaScript, 2nd Edition, specifically the sections "How Templates Work with Collection Controls" and its subsections on pages 364-376, and "Template Functions (Part 2): Optimizing Item Rendering" starting on page 414 if you want to go deeper, but probably isn't necessary for your scenario.
Custom layouts, just to note, are covered in Appendix B. Also, custom layouts only work with WinJS 2.0 on Windows 8.1; template functions work with WinJS 1.0 or 2.0 on 8.0 and 8.1 both.
Anyway, with per-item rendering via a template function, you can just create CSS as appropriate for the classes in each item structure you create, so you don't have to play games with the ListView's style hierarchy. You'll just want to make sure that your header/letter items have a suitable top margin to make the gap. Alternately, you could also have a gap item type in your data source that would render a blank space and not respond to any input. Also, you'll need to style the various selected, active, and hover states of the header/letter items to be all the same so they don't get highlighted like an app item.
The last note I'll make is that you don't need to use the GridLayout's grouping capabilities at all, because that forces the groups into new columns as you've observed. That's just its layout behavior when it has a group items source. That said, you could use a standard List for your app items and a create a group data source from it like you're doing, and then use a custom layout to handle the groups differently than the standard GridLayout. That should work as well, so it's really your choice in approach. I'd use this way if for some reason you can't get the header/letter styling to work with the template function approach, or if you're targeting Windows 8.0/WinJS 1.0 and not 8.1/2.0.
Hope that explains your options.