Tagfield scrolling not working

I use 6.0 Sometime I get a strange problem in the ExtJS 6 tag field. I use tagfield with growMax : 3 . Now that the value selected for tagfield is more than three, I get a pointer up and down in tagfield.

It's fine. Now the stitched part is when I click on the down arrow to take me exactly to the bottom of the field. I do not see that another value is selected that is between them. Is there a way to slow down the scroll speed of these values.

My fiddle: Fiddle

Step to play.

  • Select multiple values ​​(more than 2 or 3)
  • Press the pointer down. (Red frame in the image) Picture

It may skip the second third value and lead you to completion.

Note. Someday I need to do this for 100 data in a tagfield. I don’t even see that everything that I have chosen. Also I can not change the height.

Is there any event that can be clicked on the scroll buttons.

+7
javascript extjs extjs6
source share
2 answers

Refresh (actually implement inheritance)

OK, it looks like you really need an inheritance based solution. This code is obviously not an idiomatic ExtJS, but it seems to work for me. First, define a custom subclass of SingleLineTag and assign 'singleline-tagfield' as its xtype (in the section “senior answer” there is a description of the main ideas of this code)

 Ext.define('Ext.form.field.SingleLineTag', { extend: 'Ext.form.field.Tag', xtype: 'singleline-tagfield', initEvents: function () { var me = this; me.callParent(arguments); me.itemList.el.dom.parentElement.addEventListener('scroll', Ext.bind(me.zzzOnTagScroll, me)); }, zzzGetTagLastScroll: function () { var me = this; return me.zzzLastScroll = me.zzzLastScroll || { lastIndex: 0, lastTop: 0, lastTimeStamp: 0 }; }, zzzScrollToTagIndex: function (index) { var tagField = this; var lastScroll = tagField.zzzLastScroll; if (lastScroll) { var lstDom = tagField.itemList.el.dom; var childrenDom = lstDom.children; var containerDom = tagField.itemList.el.dom.parentElement; if ((index >= 0) && (index < childrenDom.length)) { lastScroll.lastIndex = index; containerDom.scrollTop = lastScroll.lastTop = childrenDom[index].offsetTop - lstDom.offsetTop; } } }, zzzOnTagScroll: function (ev) { var me = this; var lastScroll = me.zzzGetTagLastScroll(); // throttle scroll events as thy occur to often and we might scroll to much if (Math.abs(lastScroll.lastTimeStamp - ev.timeStamp) < 200) { ev.preventDefault(); return; } lastScroll.lastTimeStamp = ev.timeStamp; var lstDom = me.itemList.el.dom; var childrenDom = lstDom.children; var containerDom = me.itemList.el.dom.parentElement; var scrollTop = containerDom.scrollTop; var index = lastScroll.lastIndex; if (index >= childrenDom.length) index = childrenDom.length - 1; if (index < 0) index = 0; var lstTop = lstDom.offsetTop; if (scrollTop > lastScroll.lastTop) { // scrolling down, find next element for (; index < childrenDom.length; index++) { if (childrenDom[index].offsetTop - lstTop > scrollTop) { break; } } if (index < childrenDom.length) { // we've found the next element so change scroll position to it top me.zzzScrollToTagIndex(index); } else { lastScroll.lastIndex = childrenDom.length; lastScroll.lastTop = containerDom.scrollTop; } } else { // scrolling up, find prev element for (; index >= 0; index--) { if (childrenDom[index].offsetTop - lstTop < scrollTop) { break; } } if (index >= 0) { // we've found the prev element so change scroll position to it top me.zzzScrollToTagIndex(index); } else { lastScroll.lastIndex = 0; lastScroll.lastTop = 0; } } }, onBeforeDeselect: function (list, record) { var me = this; me.callParent(arguments); var value = record.get(me.valueField); var index = me.getValue().indexOf(value); var lastScroll = me.zzzGetTagLastScroll(); if (lastScroll.lastIndex > index) lastScroll.lastIndex -= 1; var nextIndex = (lastScroll.lastIndex > index) ? lastScroll.lastIndex - 1 : lastScroll.lastIndex; setTimeout(function () { me.zzzScrollToTagIndex(nextIndex); }, 0); }, onItemListClick: function(ev) { var me = this; // HACK for IE: throttle click events after scroll // click on the scrollbar seem to generate click on the "itemList" as well // which lead to showing of the dropdown var lastScroll = me.zzzGetTagLastScroll(); if (Math.abs(lastScroll.lastTimeStamp - ev.timeStamp) > 200) { me.callParent(arguments); } } }); 

Now change xtype in items collections

 var shows = Ext.create('Ext.data.Store', { fields: ['id', 'show'], data: [ {id: 0, show: 'Battlestar Galactica'}, {id: 11, show: 'Doctor Who'}, {id: 2, show: 'Farscape'}, {id: 3, show: 'Firefly'}, {id: 4, show: 'Star Trek'}, {id: 5, show: 'Star Wars: Christmas Special'} ] }); Ext.create('Ext.form.Panel', { renderTo: Ext.getBody(), title: 'Sci-Fi Television', height: 200, width: 300, items: [{ //xtype: 'tagfield', // old xtype: 'singleline-tagfield', // new growMax: 18, fieldLabel: 'Select a Show', store: shows, displayField: 'show', valueField: 'id', queryMode: 'local', filterPickList: true, }] }); 

Please note that if you do not adjust this element to the actual length of only one row of height, it will behave strangely in terms of scrolling.

See the combined code in the Sencha violin


Old answer

I am not very good at ExtJS, but I think that not a good answer is better than no answer at all. First of all, I agree that growMax is in pixels and therefore 3 is too small. Still considering your problem, it seems that there is not enough space for a full-scale scrollbar, so the only way is to add custom scroll logic. It's probably best to create some new class that inherits from Tag , but I'm not sure how to do it correctly in ExtJS, so here is some kind of custom and probably non-idiomatic code.

 function findComponentByElement(node) { var topmost = document.body, target = node, cmp; while (target && target.nodeType === 1 && target !== topmost) { cmp = Ext.getCmp(target.id); if (cmp) { return cmp; } target = target.parentNode; } return null; } var getTagLastScroll = function (tagField) { return tagField.zzzLastScroll = tagField.zzzLastScroll || { lastIndex: 0, lastTop: 0, lastTimeStamp: 0 }; }; var scrollToTagIndex = function (tagField, index) { var lastScroll = tagField.zzzLastScroll; if (lastScroll) { var lstDom = tagField.itemList.el.dom; var childrenDom = lstDom.children; var containerDom = tagField.itemList.el.dom.parentElement; if ((index >= 0) && (index < childrenDom.length)) { lastScroll.lastIndex = index; containerDom.scrollTop = lastScroll.lastTop = childrenDom[index].offsetTop - lstDom.offsetTop; //console.log("Scroll to " + containerDom.scrollTop); //console.log(lastScroll); } } }; var onTagScroll = function (ev) { var tagField = findComponentByElement(ev.target); var lastScroll = getTagLastScroll(tagField); // need to throttle scroll events or will scroll to much if (Math.abs(lastScroll.lastTimeStamp - ev.timeStamp) < 200) { ev.preventDefault(); return; } //console.log(ev); lastScroll.lastTimeStamp = ev.timeStamp; var lstDom = tagField.itemList.el.dom; var childrenDom = lstDom.children; var containerDom = tagField.itemList.el.dom.parentElement; var scrollTop = containerDom.scrollTop; //console.log("Before " + containerDom.scrollTop); //console.log(lastScroll); var index = lastScroll.lastIndex; if (index >= childrenDom.length) index = childrenDom.length - 1; if (index < 0) index = 0; var lstTop = lstDom.offsetTop; if (scrollTop > lastScroll.lastTop) { // scrolling down, find next element for (; index < childrenDom.length; index++) { if (childrenDom[index].offsetTop - lstTop > scrollTop) { break; } } if (index < childrenDom.length) { // we've found the next element so change scroll position to it top scrollToTagIndex(tagField, index); } else { lastScroll.lastIndex = childrenDom.length; lastScroll.lastTop = containerDom.scrollTop; } } else { // scrolling up, find prev element for (; index >= 0; index--) { if (childrenDom[index].offsetTop - lstTop < scrollTop) { break; } } if (index >= 0) { // we've found the prev element so change scroll position to it top scrollToTagIndex(tagField, index); } else { lastScroll.lastIndex = 0; lastScroll.lastTop = 0; } } //console.log("After " + containerDom.scrollTop); //console.log(lastScroll); }; var beforeDeselect = function (tagField, record) { var value = record.get(tagField.valueField); var index = tagField.getValue().indexOf(value); var lastScroll = getTagLastScroll(tagField); if (lastScroll.lastIndex > index) lastScroll.lastIndex -= 1; var nextIndex = (lastScroll.lastIndex > index) ? lastScroll.lastIndex - 1 : lastScroll.lastIndex; setTimeout(function () { scrollToTagIndex(tagField, nextIndex); }, 0); }; var attachCustomScroll = function (tagField) { var containerDom = tagField.itemList.el.dom.parentElement; containerDom.addEventListener('scroll', onTagScroll); tagField.on('beforeDeselect', beforeDeselect); }; 

You can use it just by doing something like

 var pnl = Ext.create('Ext.form.Panel', { ... }); var tagField = pnl.items.items[0]; attachCustomScroll(tagField); 

The main idea of ​​my code is to intercept the scroll event for a container element containing ul with the selected elements and handle the events not as real scrolling, but as a direction for scrolling through one element to it. The data necessary for proper operation is attached back to the widget, apparently by the unique name zzzLastScroll .

There is also an additional piece of logic to make scrolling better when some element is removed.


Full code (instead of violin)

Unfortunately, I don’t have an ExtJS account, and without it I can’t create a new script there, so just in case there will be the full code of the modified app.js , which I used to check my code.

 var shows = Ext.create('Ext.data.Store', { fields: ['id', 'show'], data: [ {id: 0, show: 'Battlestar Galactica'}, {id: 11, show: 'Doctor Who'}, {id: 2, show: 'Farscape'}, {id: 3, show: 'Firefly'}, {id: 4, show: 'Star Trek'}, {id: 5, show: 'Star Wars: Christmas Special'} ] }); var pnl = Ext.create('Ext.form.Panel', { renderTo: Ext.getBody(), title: 'Sci-Fi Television', height: 200, width: 300, items: [{ xtype: 'tagfield', growMax: 18, fieldLabel: 'Select a Show', store: shows, displayField: 'show', valueField: 'id', queryMode: 'local', filterPickList: true, }] }); window.tagField = pnl.items.items[0]; window.lstDom = window.tagField.itemList.el.dom; window.container = window.lstDom.parentElement; tagField.setValue([11, 3, 4, 5]); function findComponentByElement(node) { var topmost = document.body, target = node, cmp; while (target && target.nodeType === 1 && target !== topmost) { cmp = Ext.getCmp(target.id); if (cmp) { return cmp; } target = target.parentNode; } return null; } var getTagLastScroll = function (tagField) { return tagField.zzzLastScroll = tagField.zzzLastScroll || { lastIndex: 0, lastTop: 0, lastTimeStamp: 0 }; }; var scrollToTagIndex = function (tagField, index) { var lastScroll = tagField.zzzLastScroll; if (lastScroll) { var lstDom = tagField.itemList.el.dom; var childrenDom = lstDom.children; var containerDom = tagField.itemList.el.dom.parentElement; if ((index >= 0) && (index < childrenDom.length)) { lastScroll.lastIndex = index; containerDom.scrollTop = lastScroll.lastTop = childrenDom[index].offsetTop - lstDom.offsetTop; //console.log("Scroll to " + containerDom.scrollTop); //console.log(lastScroll); } } }; var onTagScroll = function (ev) { var tagField = findComponentByElement(ev.target); var lastScroll = getTagLastScroll(tagField); if (Math.abs(lastScroll.lastTimeStamp - ev.timeStamp) < 200) { ev.preventDefault(); return; } //console.log(ev); lastScroll.lastTimeStamp = ev.timeStamp; var lstDom = tagField.itemList.el.dom; var childrenDom = lstDom.children; var containerDom = tagField.itemList.el.dom.parentElement; var scrollTop = containerDom.scrollTop; //console.log("Before " + containerDom.scrollTop); //console.log(lastScroll); var index = lastScroll.lastIndex; if (index >= childrenDom.length) index = childrenDom.length - 1; if (index < 0) index = 0; var lstTop = lstDom.offsetTop; if (scrollTop > lastScroll.lastTop) { // scrolling down, find next element for (; index < childrenDom.length; index++) { if (childrenDom[index].offsetTop - lstTop > scrollTop) { break; } } if (index < childrenDom.length) { // we've found the next element so change scroll position to it top scrollToTagIndex(tagField, index); } else { lastScroll.lastIndex = childrenDom.length; lastScroll.lastTop = containerDom.scrollTop; } } else { // scrolling up, find prev element for (; index >= 0; index--) { if (childrenDom[index].offsetTop - lstTop < scrollTop) { break; } } if (index >= 0) { // we've found the prev element so change scroll position to it top scrollToTagIndex(tagField, index); } else { lastScroll.lastIndex = 0; lastScroll.lastTop = 0; } } //console.log("After " + containerDom.scrollTop); //console.log(lastScroll); }; var beforeDeselect = function (tagField, record) { var value = record.get(tagField.valueField); var index = tagField.getValue().indexOf(value); var lastScroll = getTagLastScroll(tagField); if (lastScroll.lastIndex > index) lastScroll.lastIndex -= 1; var nextIndex = (lastScroll.lastIndex > index) ? lastScroll.lastIndex - 1 : lastScroll.lastIndex; setTimeout(function () { scrollToTagIndex(tagField, nextIndex); }, 0); }; var attachCustomScroll = function (tagField) { var containerDom = tagField.itemList.el.dom.parentElement; containerDom.addEventListener('scroll', onTagScroll); tagField.on('beforeDeselect', beforeDeselect); }; attachCustomScroll(window.tagField); 
+4
source share

The growMax parameter must be the height in pixels. So that the space for 3 of these elements is selected with enough space to scroll, you can try to set growMax=60 . See Updated fiddle .

+1
source share

All Articles