I have an iron-list which I add new items to while scrolling to the bottom using iron-scroll-threshold. That works fine.
What I also need is a general event which is fired when scrolling stops.
I need that to know whether the listed items (m.message) have been seen by the user, by checking which items are currently visible in the view-port after scrolling, and then marking them as "read".
<div class="container" on-content-scroll="_scrollHandler">
<iron-scroll-threshold id="threshold" scroll-target="mlist" lower-threshold="500" on-lower-threshold="_loadMoreData"></iron-scroll-threshold>
<iron-list items="[[messages]]" id="mlist" as="m">
<template>
<div>
<p>[[m.message]]</p>
</div>
</template>
</iron-list>
</div>
The handler _scrollHandler is never fired however.
What would be necessary to get an event after scrolling ends?
You need the style: overflow: auto on the div.container. This will make sure the scroll event will invoke.
I could not find any such event as content-scroll, but with the changes above you should be able to change your HTML to bind against the handler like: on-scroll="_scrollHandler".
To detect if scrolling has stopped, I'd recommend using Polymer.debounce to have the callback set the isScrolling state to false like:
app._scrollHandler = function (e) {
app.isScrolling = true
app.debounce('scroll-handler', _ => {
app.isScrolling = false
}, 20)
}
It works at the end by moving on-scroll="_scrollHandler" to the iron-list:
<div class="container">
<iron-scroll-threshold id="threshold" scroll-target="mlist" lower-threshold="500" on-lower-threshold="_loadMoreData"></iron-scroll-threshold>
<iron-list items="[[messages]]" id="mlist" as="m" on-scroll="_scrollHandler">
<template>
<div>
<p>[[m.message]]</p>
</div>
</template>
</iron-list>
</div>
With the function being:
_scrollHandler: function() {
this.debounce("markAsRead", function(e) {
console.log("debounce");
}, 500);
}
Edit:
In case the iron-scroll-threshold wraps the iron-list, you need to move on-scroll to the iron-scroll-threshold-element:
<iron-scroll-threshold on-scroll="_scrollHandler" id="threshold" on-lower-threshold="_loadMore">
<iron-list scroll-target="threshold">...</iron-list>
</iron-scroll-threshold>
Related
Polymer 1.*
I had to write my own dropdown menu. I need to close the menu when the user clicks outside of the element. However, I am not able to catch the event when a user clicks outside of the element so I can close the menu.
Any ideas what I am doing wrong?
EDIT: I've studying paper-menu-button which closes paper-listbox when I click outside the element.... but I don't see anywhere where it catches that event https://github.com/PolymerElements/paper-menu-button/blob/master/paper-menu-button.js#L311
<dom-module id="sp-referrals-reservations-dropdown">
<template>
<style include="grid-dropdown-styles">
</style>
<div id="dropdown" class="grid-dropdown">
<paper-listbox>
<div class="grid-dropdown-item">Convert to stay</div>
<div class="grid-dropdown-item">Cancel reservation</div>
<div class="grid-dropdown-item">Delete reservation</div>
</paper-listbox>
</div>
</template>
<script>
(function() {
'use strict';
Polymer({
is: 'sp-referrals-reservations-dropdown',
behaviors: [Polymer.IronControlState],
properties: {
},
listeners: {
'tap': '_close',
'click': '_close',
'blur': '_close',
'focusout': '_close',
'focusChanged': '_close',
'focus-changed': '_close',
'active-changed': '_close',
'activeChanged': '_close',
'iron-activate': '_close',
'ironActivate': '_close',
},
open: function(e) {
},
_close: function() {
console.log('aaa');
this.$.dropdown.style.display = "none";
},
});
})();
</script>
</dom-module>
I am not sure, will it be enough, but if you wrap the sp-referrals-reservations-dropdown element with a parent-element then you can listen to parent-element events same as its child.
<parent-element></parent-element>
<dom-module id="parent-element">
<template>
<style>
:host {
display:block;
background:green;
width:100%;
height:100vh; }
</style>
<sp-referrals-reservations-dropdown id="spref"></sp-referrals-reservations-dropdown>
At parent's script:
Polymer({is:'parent-element', properties:{},
listeners:{ 'tap': '_taped'},
_taped:function(t){
this.$.spref._close();
}
});
this _taped functions will call child's _close function. Hope its help.
Incase of needed more. We can develop this.
Demo
EDIT
Wrap your element into paper-dialog. And at ready:function() call
this.$.dialog.open()
Then when you click outside of the element. paper-dialog will close automatically.
Just FYI, you weren't able to get this to work because custom elements don't listen for events outside of their own encapsulation unless you explicitly wire them up to do so... and if you do so, you can't use Polymer's built-in event handling.
So something like this would work:
// Insert this somewhere it'll get run once attached to the DOM
// This line keeps clicks within your element from closing the dropdown
this.shadowroot.addEventListener('click', (event) => { event.stopPropagation(); });
// And this listens for any click events that made it up to body, and closes the element
document.querySelector('body').addEventListener('click', this._close);
Or, at least, that's what I think was going on. The polymer elements either did it by binding to a different event (blur?) or by having the parent element trigger an event on click that told the child element to close.
I intend to implement an infinite scroller using with PSK v3 by setting the scroll-target to "document", but it just doesn't work for PSK. Below is the code snippet:
<iron-scroll-threshold id="threshold"
scroll-target="document"
on-lower-threshold="queryMoreData">
<iron-list items="[[items]]" grid>
<template>
<div>
<div class="content">
item: [[item.n]]
</div>
</div>
</template>
</iron-list>
</iron-scroll-threshold>
I have tested scroll-target="document" with plain HTML, and it's working nicely. I want to know how should it be set for PSK to trigger an event when user scroll to the end of a particular page/view.
Apparently, you need to set the scroll-target of <iron-scroll-threshold> to the scroll-target of <app-header> with the following code in ready():-
ready() {
{ // Set scroll target for <iron-scroll-threshold>
let myApp = document.querySelector('my-app');
let appHeader = myApp.shadowRoot.querySelector('app-header');
this.$.threshold.scrollTarget = appHeader.scrollTarget;
}
}
Sometimes, your scroll position is at enough lower place at start than queryMoreData functions fire ones. In this function, you will need to clear the threshold by calling the clearTriggers method into queryMoreData function :
queryMoreData() {
//check if you have more data to load than
ironScrollTheshold.clearTriggers();
}
After which it will begin listening again for the scroll position to reach the threshold again assuming the content in the scrollable region has grown.
You have to set the scroll-target attribute of the iron-scroll-threshold element to 'document'.
Also note the scroll-target attribute of the iron-list is set to the id of the iron-scroll-threshold element - 'threshold'.
<iron-scroll-threshold scroll-target="document" id="threshold" on-lower-threshold="loadMoreData" lower-threshold="100">
<iron-list id="list" items="[[items]]" as="item" scroll-target="threshold" grid>
<!-- template -->
</iron-list>
</iron-scroll-threshold>
I am trying to use an iron-list (and iron-scroll-threshold) within a app-header-layout with has-scrolling-region.
I generated the basic app layout with the polymer-CLI.
If I do not use has-scrolling-region on the app-header-layout and use "document" for scroll-target on the iron-list it kinda works. But with this solution the scrollbar belongs to the window and does not slide beneath the header and I obviously cannot get the nice "waterfall" behaviour that is usually associated with these kinds of layouts.
Therefore, I use has-scrolling-region on the app-header-layout, but what is the right way to pass the corresponding scoller to the scroll-target property of the iron-list?
<!-- Main content -->
<app-header-layout has-scrolling-region id="layout">
<app-header condenses reveals effects="waterfall">
<app-toolbar>
<paper-icon-button icon="menu" drawer-toggle></paper-icon-button>
<div title>Twiamo</div>
</app-toolbar>
</app-header>
<iron-pages role="main" selected="[[page]]" attr-for-selected="name" id="page">
<my-iron-list name="view1" scroll-target="[[_getScrollTarget()]]"></my-iron-list>
<my-view2 name="view2"></my-view2>
<my-view3 name="view3"></my-view3>
</iron-pages>
</app-header-layout>
I looked into the implementation of app-header-layout to find the right element. This expression actually yields me the right element and everything works fine.
_getScrollTarget: function() {
return this.$.layout.shadowRoot.querySelector("#contentContainer");
}
But there has to be a better, a right way? Grabbing into the shadow DOM of the app-header-layout is not exactly using "public interface"!
To complete the example, here my code for my-iron-list. My-iron-list wraps and iron-list, iron-scroll-theshold, and some dummy data provider stuff. The scroll-target on my-iron-list is just passed to the iron-list and iron-scroll-threshold within my-iron-list:
<dom-module id="my-iron-list">
<template>
<iron-list items="[]" as=item id="list" scroll-target="[[scrollTarget]]">
<template>
<div class="item">[[item]]</div>
</template>
</iron-list>
<iron-scroll-threshold
id="scrollTheshold"
lower-threshold="100"
on-lower-threshold="_loadMoreData"
scroll-target="[[scrollTarget]]">
</iron-scroll-threshold>
</template>
<script>
Polymer({
is: 'my-iron-list',
properties: {
page: {
type : Number,
value : 0
},
perPage: {
type : Number,
value : 100
},
scrollTarget: HTMLElement,
},
_pushPage: function() {
for (i = 0; i < this.perPage; i++) {
this.$.list.push('items', 'Entry number ' + (i+1+this.page*this.perPage));
}
},
_loadMoreData: function() {
this._pushPage();
this.page = this.page + 1;
this.$.scrollTheshold.clearTriggers();
}
});
</script>
</dom-module>
I have the same problem as you, for now the cleanest anwser I have was to use the app-header scrollTarget.
In your case move add an id to the app-header
<app-header condenses reveals effects="waterfall" id="header">
<app-toolbar>
<paper-icon-button icon="menu" drawer-toggle></paper-icon-button>
<div title>Twiamo</div>
</app-toolbar>
</app-header>
and then instead of
_getScrollTarget: function() {
return this.$.layout.shadowRoot.querySelector("#contentContainer");
}
just use the scrollTarget property
_getScrollTarget: function() {
return this.$.header.scrollTarget;
}
If you found out a better way let me know.
Cheers,
I struggled with the same issue. While I was using iron-scroll-target-behavior instead of iron-scroll-threshold, I still needed to pass a scroll-target reference to an element inside a app-layout-header.
If has-scrolling-region is true, app-header-layout sets the scroll-target to be an internal div with an ID of #contentContainer. You can target this div and pass it as the scroll-target to your iron-list.
You would just need to alter the _getScrollTarget function inside your original code.
_getScrollTarget: function() {
return this.$.layout.$.contentContainer;
}
Hope it helps!
If anyone is coming here for an answer in 2017, I'm just letting you know that the same issue persists in Polymer 2.0.
I was able to overcome the issue by having the following code in my app shell (eg. PSK's my-app.html):
First, put an id attribute of 'layout' on your app-header-layout element.
Next, add this to your Polymer class (in your my-app.html equivalent):
static get properties() {
return {
scrollTarget: HTMLElement,
}
}
ready() {
super.ready();
this.scrollTarget = this.$.layout.shadowRoot.querySelector("#contentContainer");
}
Then, pass in the property to a scroll-target attribute on your lazy-loaded pages:
<my-page scroll-target="[[scrollTarget]]"></my-page>
Finally, in your lazy-loaded pages (eg. my-page):
<iron-list scroll-target="[[scrollTarget]]"></iron-list>
...
static get properties() {
return {
scrollTarget: HTMLElement,
}
}
This isn't an ideal solution, but it works.
I have this polymer element in which a list of items are shown. I am populating the list with iron-list. Each list item contains a paper-card, which hides some other custom elements, when i click a button, the corresponding view will display. Everything upto this part is working fine.
The problem i'm having is, When i show a custom element, or hide the element, the iron-list item height wont update. When i show the element, the element will show behind other views, and when i hide, a white space is appearing with height equal to that of the showed view.
below is the code for the list,
<div on-click="clicked" id="feedlist">
<iron-list items="{{feedList}}" as="item">
<template>
<card-element field="{{item.id}}" feed-id="{{item.id}}" feed-number="{{item.id}}" feed-title="{{item.title}}" feed-name="{{item.name}}" date="{{item.timeStamp}}"></card-element>
</template>
</iron-list>
and code for this card-element is as given below
<div class="content" id="content">
<text-input id="note" type="1" note="{{response.note}}" feed-id="[[feedId]]"></text-input>
<data-viewer id="dataViewer" plugin-data="{{response.data}}" feed-id="[[feedId]]" default-view="gridView" parent="feed"></data-viewer>
<comment-view id="comment" comments="{{response.comment}}" feed-id="[[feedId]]"></comment-view>
</div>
im hiding and showing, text-input, data-viewer and comment-view or all at once according to button clicks.
Below is the code for toggling the data-viewer,
toggleDataView: function (event) {
event.stopPropagation();
if (this.isFeed) {
this.isDataView = false;
this.isFeed = false;
}
this.isDataView = !this.isDataView;
if (this.isDataView) {
this.isComment = false;
this.isNote = false;
this.$.note.hide();
this.$.comment.hide();
this.$.dataViewer.show();
this._showDragView();
} else {
this.$.dataViewer.hide();
this._hideDragView();
}
},
I think you need to call updateSizeForItem(item) https://elements.polymer-project.org/elements/iron-list#method-updateSizeForItem after you update an item (view, hide, ...)
My keypresses (right and left keys) only throw events when I've clicked in certain parts of my page. How do I make it so my iron-a11y-keys work on the entire page?
Here's what I have now:
<template>
<iron-a11y-keys keys="left right" on-keys-pressed="onRightKey"></iron-a11y-keys>
<paper-drawer-panel id="drawerPanel" responsive-width="1024px" drawer-width="{{drawerWidth}}">
...
</paper-drawer-panel>
</template>
It seems to behave the same way when I set target={{}}. I'm not certain what the target parameter does so that may be my problem. A bit of education on that would also be appreciated.
<template>
<iron-a11y-keys id="a11y" keys="left right" on-keys-pressed="onRightKey"></iron-a11y-keys>
<paper-drawer-panel id="drawerPanel" responsive-width="1024px" drawer-width="{{drawerWidth}}">
...
</paper-drawer-panel>
</template>
and in the script:
ready: function() {
this.$.a11y.target = document.querySelector('body');
}