diff --git a/components-chromium/iron-list/iron-list.js b/components-chromium/iron-list/iron-list.js
index 67ec9a735773d..ad8e118645b56 100644
--- a/components-chromium/iron-list/iron-list.js
+++ b/components-chromium/iron-list/iron-list.js
@@ -207,19 +207,6 @@ to previous and next items in the list:
</iron-list>
```
-### Styling
-
-You can use the `--iron-list-items-container` mixin to style the container of
-items:
-
-```css
-iron-list {
- --iron-list-items-container: {
- margin: auto;
- };
-}
-```
-
### Resizing
`iron-list` lays out the items when it receives a notification via the
@@ -267,7 +254,6 @@ Polymer({
}
#items {
- @apply --iron-list-items-container;
position: relative;
}
@@ -376,7 +362,15 @@ Polymer({
* there's some offset between the scrolling element and the list. For
* example: a header is placed above the list.
*/
- scrollOffset: {type: Number, value: 0}
+ scrollOffset: {type: Number, value: 0},
+
+ /**
+ * If set to true, focus on an element will be preserved after rerender.
+ */
+ preserveFocus: {
+ type: Boolean,
+ value: false
+ }
},
observers: [
@@ -1066,11 +1060,53 @@ Polymer({
newGrid && this._updateGridMetrics();
},
+ /**
+ * Finds and returns the focused element (both within self and children's
+ * Shadow DOM).
+ * @return {?HTMLElement}
+ */
+ _getFocusedElement: function() {
+ function doSearch(node, query) {
+ let result = null;
+ let type = node.nodeType;
+ if (type == Node.ELEMENT_NODE || type == Node.DOCUMENT_FRAGMENT_NODE)
+ result = node.querySelector(query);
+ if (result)
+ return result;
+
+ let child = node.firstChild;
+ while (child !== null && result === null) {
+ result = doSearch(child, query);
+ child = child.nextSibling;
+ }
+ if (result)
+ return result;
+
+ const shadowRoot = node.shadowRoot;
+ return shadowRoot ? doSearch(shadowRoot, query) : null;
+ }
+
+ // Find out if any of the items are focused first, and only search
+ // recursively in the item that contains focus, to avoid a slow
+ // search of the entire list.
+ const focusWithin = doSearch(this, ':focus-within');
+ return focusWithin ? doSearch(focusWithin, ':focus') : null;
+ },
+
/**
* Called when the items have changed. That is, reassignments
* to `items`, splices or updates to a single item.
*/
_itemsChanged: function(change) {
+ var rendering = /^items(\.splices){0,1}$/.test(change.path);
+ var lastFocusedIndex, focusedElement;
+ if (rendering && this.preserveFocus) {
+ lastFocusedIndex = this._focusedVirtualIndex;
+ focusedElement = this._getFocusedElement();
+ }
+
+ var preservingFocus = rendering && this.preserveFocus && focusedElement;
+
if (change.path === 'items') {
this._virtualStart = 0;
this._physicalTop = 0;
@@ -1082,7 +1118,7 @@ Polymer({
this._physicalItems = this._physicalItems || [];
this._physicalSizes = this._physicalSizes || [];
this._physicalStart = 0;
- if (this._scrollTop > this._scrollOffset) {
+ if (this._scrollTop > this._scrollOffset && !preservingFocus) {
this._resetScrollPosition(0);
}
this._removeFocusedItem();
@@ -1114,6 +1150,17 @@ Polymer({
} else if (change.path !== 'items.length') {
this._forwardItemPath(change.path, change.value);
}
+
+ // If the list was in focus when updated, preserve the focus on item.
+ if (preservingFocus) {
+ flush();
+ focusedElement.blur(); // paper- elements breaks when focused twice.
+ this._focusPhysicalItem(
+ Math.min(this.items.length - 1, lastFocusedIndex));
+ if (!this._isIndexVisible(this._focusedVirtualIndex)) {
+ this.scrollToIndex(this._focusedVirtualIndex);
+ }
+ }
},
_forwardItemPath: function(path, value) {
@@ -1851,13 +1898,7 @@ Polymer({
if (!targetModel) {
return;
}
- if (focusedModel === targetModel) {
- // If the user focused the same item, then bring it into view if it's not
- // visible.
- if (!this._isIndexVisible(fidx)) {
- this.scrollToIndex(fidx);
- }
- } else {
+ if (focusedModel !== targetModel) {
this._restoreFocusedItem();
// Restore tabIndex for the currently focused item.
if (focusedModel) {