frend.co
frend.co copied to clipboard
Introduce event callback option for all components
A lot of quality plugins have event emitter setups, but that might be a bit overkill for Frend bits.
Would be nice for this to be consistent amongst components. Is it as simple as adding an onToggle
property to the options object that takes a function and passes the instance?
var myTabs = Frtabs({
onToggle: function (tabs) {
// do something
}
});
As part of this, do we need to start storing things like the active element on each instance, so they can be referenced in these callbacks? I.e. tabs.activeTab
Initial tester, any idea how we're going to return the instance?
Has there been any more development on this? I'm specifically trying to use the off canvas component, but discovered there aren't any open and close events I can hook into, which is surprising.
Hey @Ambient-Impact - thanks for enquiring on this. Unfortunately we lost a bit of steam on v2 with other obligations. In hindsight, exposing show/hide methods in the original API would have been super helpful.
Were you hoping to fire events when opening/closing the off-canvas panel? In which case, binding your own event handlers on the buttons will do the trick: https://codepen.io/adamduncan/pen/JLaoRM?editors=0010
Manually invoking open/close from elsewhere in your code would also be possible, but at this point would be a case of replicating the logic in _showPanel
and _hidePanel
I'm afraid.
Hope that helps.
No worries. I started to replicate those event handlers, but then realized I also needed to account for the user hitting the escape key or clicking outside of the panel, and it started to get more complicated. I'm implementing an overlay (and using ally.js to trap focus in the off canvas panel), so I need a way to accurately know when the panel is opened and closed. After a bit of tinkering, I decided to use MutationObserver to watch for changes to aria-hidden
on the panel. I'll probably just fall back to not using an overlay if it isn't supported. It still feels like a bit of a hack, but it's the next best thing.
Sure, I see. Sorry about that. MutationObserver sounds like a nice, modern approach.
Other a11y packages (like Micromodal) have nice open/close event APIs. If you're implementing the off-canvas in conjunction with a modal overlay, I wonder whether the case could be made for treating the off-canvas as a modal-of-sorts as well 🤔
@Ambient-Impact I've done something similar using
$(opts.contentSelector).on('transitionend', function()... )
which was better supported than MutationObserver when it was written (I'd rather use MutationObserve today)
results here: https://italia.github.io/design-web-toolkit/components/detail/offcanvas.html
Nice one @gunzip!
Hey @Ambient-Impact I am using MutationObserver, but it fires twice. Do you have the same problem?
I have the following script to activate a fadIn/fadeOut when the offCanvas opens/closes:
// Offcanvas
var myOffcanvas = Froffcanvas();
// Listen to offCanvas close/open
var offCanvasObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.type == "attributes") {
if (mutation.target.getAttribute('aria-hidden') == "true") {
$('.overlay-offcanvas').fadeOut( 60 );
console.log('closes');
} else {
$('.overlay-offcanvas').fadeIn( 60 );
console.log('open');
}
}
});
});
var config = {
attributes: true, //configure it to listen to attribute changes
attributeFilter: ['aria-hidden'] // filter your attributes
};
// offCanvasObserver.observe(document.getElementById('offcanvas-sidebar'), config);
offCanvasObserver.observe(document.getElementById('offcanvas-cart'), config);
`
There must be something firing it twice, but I can not find what it is. I have two offCanvas, and they don't work well together.
@kevinmamaqi I've written my own complex wrapper around Froffcanvas, and it seems to work fine with more than one offcanvas on the page. With regards to your issue with MutationObserver, I think it might be due to the attribute changing more than once and the observer picking that up. There's no guarantee that it won't fire more than once, so what I've done is to instruct it to also keep the old value of aria-hidden
, so I can compare them and only do something if it goes from true
to false
or vice versa. The below is a private function from my wrapper, hope it helps:
/**
* Bind events so that we can provide open and close events.
*
* Froffcanvas doesn't currently offer any events, so we use a
* MutationObserver watching for changes to the 'aria-hidden' attribute to
* determine if the panel has been opened or closed. See link for
* Froffcanvas issue. If MutationObserver is not supported by the browser,
* no events will be fired.
*
* @param {jQuery} $panel
*
* @link https://github.com/frend/frend.co/issues/70
* @link https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
*/
function bindEvents($panel) {
if (!('MutationObserver' in window)) {
return;
}
var panel = $panel[0];
panel.aiOffcanvas.mutationObserver =
new MutationObserver(function(mutations) {
var action;
for (var i = 0; i < mutations.length; i++) {
var mutatedPanel = mutations[i].target;
if (
mutations[i].oldValue === 'false' &&
mutatedPanel.getAttribute('aria-hidden') === 'true'
) {
action = 'closed';
} else if (
mutations[i].oldValue === 'true' &&
mutatedPanel.getAttribute('aria-hidden') === 'false'
) {
action = 'opened';
}
}
switch (action) {
case 'opened':
openEvent($panel);
break;
case 'closed':
closeEvent($panel);
break;
}
});
panel.aiOffcanvas.mutationObserver.observe(panel, {
attributes: true,
attributeFilter: ['aria-hidden'],
attributeOldValue: true
});
};