angular-inview
angular-inview copied to clipboard
Detect active tab
Is there a way for inview to detect whether the object is actually visible?
I'm pretty sure there's no way to know whether another window obstructs the visibility, but maybe there's a way to know whether the tab is actually active, or whether another tab of the same window is opened.
Here are some resources that might help: https://stackoverflow.com/questions/1760250/how-to-tell-if-browser-tab-is-active
there is for sure a way to detect if a window/tab is active. how would you see this fit for this directive though? Could you give a code example of how you'd use the directive?
I see different possibilities. Either a separate attribute:
<div in-view="$inview && ctrl.isVisible()" strict-visibility="true"></div>
Or an attribute on $inviewInfo
:
<div in-view="$inview && $inviewInfo.hasFocus && ctrl.isVisible()"></div>
In any case it would mean that inview would have to trigger a change event if the tab/window focus changes. If you don't want to do that by default for perforamnce reasons, maybe that could be implemented as a configuration, either via attribute or using a provider.
It would certainly be useful if inview would detect focus, as some things don't need to be done if the window is not focused, saving energy on mobile devices. Another use case is "read notifications" if a user looks at a certain element. If a window is out of focus, a read notification should not be triggered, even if the object is inside the viewport.
Perhaps you could apply it to the in-view-container
. In this case the container would be the tab, and so, if specified, you check to see if the container is visible?
Also, using the ClientRect is a pretty good heuristic for whether the element is truly visible. If all it's values are 0 (i.e. it's right at the top of the screen with 0 width and height) then it's quite likely to not really be visible.
vm.initIfVisible = $inviewInfo => {
let rect = $inviewInfo.elementRect,
keys = ['height', 'width', 'top', 'bottom', 'left', 'right'],
isVisible = keys.map(key => rect[key]).some(value => value > 0);
if(isVisible) {
vm.init();
}
};
However this still doesn't work nicely because interactions with tabs occur on click events, not scroll. So you'd need to also have a click listener which triggered another check. For that reason you'd almost certainly want to have this as an optional directive / option.
Alternatively you can also use $inviewInfo.element[0].offsetParent
- but again, you won't know when to initialise it after clicking the tab.
So after looking at this a little more, hopefully I have a solution (above).
Adding the requireOffsetParent
to in-view-options
should address the issue by only regarding an element as in view if it both overlaps with the viewport boundingRect and it has an offsetParent.
I chose not to make it the default behaviour because:
- An offsetParent is never reported for a fixed positioned element - you'd need to make additional checks for it
- Putting it behind an optional flag is safer as I don't know all the nuances of when it should normally trigger
Regarding this issue: Checking for document.hidden === false
and/or document.visibilityState === 'visible'
should suffice.
https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
I'll give it a stab, but might need your help.
A fix for this is waiting in #122. Any chance to get that merged?
We're using angular-inview in Threema Web, where the ability to detect active tabs would help to improve usability.