joshwnj / react-visibility-sensor Goto Github PK
View Code? Open in Web Editor NEWSensor component for React that notifies you when it goes in or out of the window viewport.
License: MIT License
Sensor component for React that notifies you when it goes in or out of the window viewport.
License: MIT License
Dear
thank you for your past contribution to this project! If you would be interested to become a collaborator, I would love to collaborate with you :) Just put your name down and we can talk plans.
Next week I'll kick off a discussion around next steps, including:
Thanks again for your help
Hello.
onChange
reports isVisible === true
even though this DOM display style is none
.
Would you consider supporting the check of the display
style?
// visibility-sensor.js
if(this.props.displayCheck) {
var elStyle = window.getComputedStyle(el);
isVisible = isVisible && elStyle.display !== 'none';
}
<VisibilitySensor displayCheck={ true } onChange={ whatever }>
<div style={ {display: 'none'} }> Testing Div </div>
</VisibilitySensor>
Hi, We are using react 0.14.7
, we will not be able to upgrade the version. When I try to use this component, I get following warning
Warning: Failed propType: Calling PropTypes validators directly is not supported by the `prop-types` package. Use `PropTypes.checkPropTypes()` to call them. Read more at http://fb.me/use-check-prop-types
To explain this better I'll talk about my use case: after a certain point in the page, I want to display two floating action buttons on the bottom of the page (mobile only - material design like), so for me it's only interesting to calculate the bottom
, not all directions.
Looking to kickstart some discussion around modernising the code base and future features
create-react-class
- much easier for newcomers to React to understand the code with this, plus it's increasingly the recommended way across the ecosystemlet/const
, arrow functions etc)setState(fn)
form rather than the setState(obj)
form in anticipation of priorities in fiberrequestAnimationFrame
for high performance scroll listenersWe're using react-visibilty-sensor to animate pictures on our home page, and we use https://sentry.io to monitor errors on client side.
We've got multiple reports on the following issue: "Invariant Violation: findDOMNode was called on an unmounted component"
This happens in visibility-sensor.js, there:
/**
* Check if the element is within the visible viewport
*/
check: function () {
var el = ReactDOM.findDOMNode(this);
var rect;
var containmentRect;
(on Chrome mobile 56.0.2924)
I can't reproduce on my computer, but looks related to the tabs "Je suis propriétaire / Je suis acquéreur".
What I understand from your code is that at the end there's no real issue, because if you can't find the node you do nothing. But that warning is a bit annoying.
I presume the issue comes from a last check()
call by "debounceCheck", because you don't clear the setTimeout l.21 on stopWatching
Hi @joshwnj, community members,
Thanks for building and maintaining this package. It has turned out to be of great help !
--
In a project I'm working on, I need to track the visibility of div
s that stay on screen. The vertical height of each div might be greater than that of the viewport
(window in my case), and hence I'm using :
partialVisibility={'top'} // also tried true
offset={{top: 100}} // also tried negative offset on bottom
What I want to achieve is if more than k
px from the top is in the viewport, than mark it as visible.
I've created a small widget to check the visibility in real time. And my code above doesn't seem to work. Here is the wrapper that I use over the visibility sensor :
import React from 'react';
import ReactVisibilitySensor from 'react-visibility-sensor';
class VisibilitySensor extends React.Component {
render() {
return (<div>
<ReactVisibilitySensor
onChange={this.props.onChange}
scrollCheck={true}
resizeCheck={true}
scrollThrottle={100}
intervalDelay={8000}
partialVisibility={'top'}
offset={{
top: 100
}}
>
{this.props.children}
</ReactVisibilitySensor>
</div>);
}
}
export default VisibilitySensor;
And here is a screenshot of my app :
(As you can see on the right, no page is marked visible, I want the partial page in viewport, to be marked visible)
What am I doing wrong here ?
Thanks.
npm run build-example
> [email protected] build-example /Users/dan/p/react-visibility-sensor
> browserify -t reactify -o example/dist/bundle.js example/main.js
events.js:141
throw er; // Unhandled 'error' event
^
Error: ENOENT: no such file or directory, open 'example/dist/bundle.js'
If your element is taller than the page, and it's top is above the page, and it's bottom is below the page, partialVertical can still end up as false.
Here's the problem:
var partialVertical =
(rect.top >= containmentRect.top && rect.top <= containmentRect.bottom)
|| (rect.bottom >= containmentRect.top && rect.bottom <= containmentRect.bottom);
I suggest adding
|| (rect.top <= containmentRect.top && rect.bottom >= containmentRect.bottom);
I'm not sure if this is a bug or a wrong usage issue.
Task: call view
method if div is in the viewport.
My implementation:
render() {
const onChange = function (isVisible) {
return isVisible ? this.view : null;
};
return (
<VisibilitySensor onChange={onChange} containment>
<div className="thediv"></div>
</VisibilitySensor>
)
}
I get this.props.containment.getBoundingClientRect is not a function
in the console. What am I doing wrong?
Attempted using this nice component with Require Js using UMD way
var React = (typeof window !== "undefined" ? window['React'] : typeof global !== "undefined" ? global['React'] : null);
var ReactDOM = (typeof window !== "undefined" ? window['ReactDOM'] : typeof global !== "undefined" ? global['ReactDOM'] : null);
With my basic understanding, i managed to get it to work if above piece of code somehow checks require('react') and require('react-dom')
.
Request suggestion to solve this.
Hey @joshwnj,
First, thanks for this usefull component 👍
I got this warning:
Warning: VisibilitySensor.getDOMNode(...) is deprecated. Please use ReactDOM.findDOMNode(instance) instead.
I have a div which has many children and div has overflow-x scroll, as soon as parents div comes in viewport it fires isVisible true for all child, but I want isVisible true only when child elements comes in visible part of viewport.
Hi, can we add posibilty to check whether the element is in viewport relatively to an optionally passed parent element? This is for scrollable containers, when you want to know if an element is outside of the visible area and container needs to be scrolled.
This would be amazingly useful as a mixin, rather than a component, so you can do
var MyComponent = React.createClass({
mixins: [
require("react-visibility-sensor")
],
render: function() {
if(this.state.visible) { ... }
else { ... }
},
...
});
where the mixin taps into getInitialState
to add the visible
property, and the componentDidMount
and componentWillUnmount
to add/remove the event listeners, respectively
When I have an element that is bigger that the viewport the onChange
will never trigger with true
.
That's because we're doing the following check:
var visibilityRect = {
top: rect.top >= containmentRect.top,
left: rect.left >= containmentRect.left,
bottom: rect.bottom <= containmentRect.bottom,
right: rect.right <= containmentRect.right
};
So in any given moment the rect.bottom <= containmentRect.bottom && rect.top >= containmentRect.top
will be true.
The solution is to change state to be some kind of enum (NONE
, TOP
, BOTTOM
, COMPLETE
), so we'll able to trigger onChange(true)
when TOP && BOTTOM
exists.
Have an issue with the versions >= 3.9.0, most probably because of 'Migrated deprecated React.PropTypes and React.createClass' update.
Example: using the visibility sensor at url https://unpkg.com/[email protected]/dist/visibility-sensor.js after the react,js script I get:
Uncaught Error: Cannot find module 'react'
at s (visibility-sensor.3.10.1.js:1)
at visibility-sensor.3.10.1.js:1
at Object.3../factory (visibility-sensor.3.10.1.js:779)
at s (visibility-sensor.3.10.1.js:1)
at visibility-sensor.3.10.1.js:1
at Object. (visibility-sensor.3.10.1.js:1737)
at Object.14../lib/is-visible-with-offset (visibility-sensor.3.10.1.js:2057)
at s (visibility-sensor.3.10.1.js:1)
at e (visibility-sensor.3.10.1.js:1)
at visibility-sensor.3.10.1.js:1
Also using the umd versions for:
react-15.6.1
mobx.3.2.2
mobx-react.4.2.2
babel-standalone.6.23.1
babel-polyfill.6.23.0
If I perform something as simple as
this.setState({
internal_visibility: visibility
}, function() {
console.log("Hello from follow up method");
});
It triggers this callback again and again with given delay. Is this by design? If I don't do anything inside the callback function except for a console.log message, it handles it the way I expect it to, i.e. the callback function is not fired except when viewport / window changes / scrolls.
Also noticed that isVisible state variable becomes null inside VisibilitySensor component whenever I change a state variable on the callback function in parent.
Is it possible to move the 'gh-pages' module in devDependencies please?
Is there any real need to wrap the children in a div
?
React.findDOMNode
will work just fine even if you just return this.props.children
from render
.
For extra safety, you can return React.Children.only(this.props.children)
. This will force the consumer to supply a single child (which is the DOM node that will be tracked).
ERROR Warning: getDefaultProps is only used on classic React.createClass definitions. Use a static property named defaultProps
instead.
This is a more philosophical question about the API of this lib
Currently, the component depends on its effects propagating upwards by a lifecycle-ish onChange
method, which usually triggers a change in state of the parent component / update store etc. However, this approach merges together the configuration of the component (scrollDelay
, intervalDelay
etc), with its effects (onChange
), which makes it slightly confusing to grok IMO.
Inspired by React Router v4, I propose the following API
const MyVisibilitySensor = createSensor({
delayInterval : 200,
// other opts
});
const App = () => (
<MyVisibilitySensor>
{
({ isVisible }) => (
isVisible ? <MainImage /> : <PlaceholderImage />
)
}
</MyVisibilitySensor>
);
This separates concerns very nicely, and also avoids the overhead of having a situation where we have a hierarchy of parent component -> visibility sensor, and the onChange
callback triggers a re-render from the parent component downwards. It helps avoid one layer of lifting state up, and is easier to read IMO.
Thoughts?
Suggested by @Andarist in #54 (comment)
What would you say about implementing IntersectionObserver support? It could even be the default with scroll and interval being used only as a fallback settings. Its already implemented in 2 browsers.
https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
It would be cool to try this, and compare to the current approach. We can try the polyfill for browsers that don't support it yet:
https://github.com/WICG/IntersectionObserver/tree/gh-pages/polyfill
Probably it's time to update react version number in package.json?
Since I replaced reactify by babelify, I get the following error when I try to browserify my project using your module:
Error: Cannot find module 'reactify' from '/Users/mvila/Projects-next/avc-online/node_modules/react-visibility-sensor'
I think you should remove the reactify transform from your package.json and find another way to reactify your tests/examples.
In case responsive website does not handle visibility while resize page.
Scenario:
componentDidMount
with active
flag so watching beginspartialVisibility
on I get isVisible
and onChange
firesvisibilityRect.bottom
which is false
at the moment - my component had no chance yet to scroll downcomponentDidUpdate
this.state.isVisible !== isVisible
to fire callback again and its previous run get me true
cause of the partialVisibility
and once again its true
cause nothing changesHowever for my use case it is a change I need to detect.
I have in my mind couple workaround for this issue, but I think this should be somehow tackled by a library and I can fix it, but aint sure what kind of fix should I apply. Any ideas @joshwnj?
Hello, I tried out different combinations of direction and positive/negative values for the offset but it seems it doesn't work.
If I have the offset enabled the sensor never fires.
For elements that do not have a size in the DOM, react-visibility-sensor
currently reports that it is visible.
I think this should be the first check:
if (rect.top + rect.right + rect.bottom + rect.left + rect.width === 0) {
isVisible = false;
}
For those of use who work on projects that aren't using commonJS yet. Tnx.. Great project bdw!
I am working on making my pages embeddable via iframes. So far it works perfectly on every device except iOS.
The react visibility sensor seems to be firing off at the wrong times, making the experience very poor: https://happiness2others.herokuapp.com/testimonials (view example on iOS device).
On my test iPhone, it's firing off when I only scrolled a little bit, which kills the infinite scroll.
Any ideas on why it's doing this?
Hello.
I'm a big fan of ur package and I really have used it well.
but, recently I tried to upgrade package version.
then I met peer dependency problem with react 15.3.1.
below is the error message.
└── UNMET PEER DEPENDENCY [email protected] invalid
and it installed 3.2.1 version. (what I tried was 3.4.0)
and I use [email protected] at node v6.5.0
Instead of using a timer it would be great (and CPU saver...) to use 'scroll' and 'resize' DOM events to check the visibility only when it's necessary. https://github.com/Pomax/react-component-visibility does that but it doesn't have the 'containment' feature.
@kof & @gaearon, continuing from #13 I'd like to get your thoughts on how to make a smooth transition.
I like the improved transparency of assigning child DOM nodes directly, rather than passing className
and style
as props. But I understand there may be more implications to the change than I've thought through.
@kof could you please give some details about what broke in your code with the upgrade, and whether you see any other concerns or blockers to making a smooth transition?
I'm trying to do an infinite scrolling pagination with the code below. I have a leftPanel component which is within a dashboard layout, so I thought by setting the containment will be fine. The problem is the onChange method called during the first render, I wonder what's wrong.
import { Component, PropTypes } from 'react'
// helpers
import { isEmpty, map, isEqual } from 'lodash'
import { routeUrl } from '~/utils/stringHelper'
// components
import { Link } from 'react-router'
import EmptyState from '~/components/common/EmptyState'
import Loading from '~/components/common/Loading'
import VisibilitySensor from 'react-visibility-sensor'
//styles
import './index.less'
class LeftPanel extends Component {
constructor() {
super()
this.state = {
page: 1,
noMore: false
}
}
componentWillReceiveProps(nextProps) {
if(isEmpty(nextProps.conversations)){
this.setState({ page: 1 })
}
}
componentDidMount() {
this.container = document.getElementById('subjects-container')
}
componentWillUnmount() {
}
handleLoaderInview = () => {
console.log('call') // triggered on the first time although the loader is not visible
if(this.props.conversations.length !== this.props.conversationsTotal) {
this.setState({
page: this.state.page + 1
}, () => {
console.log('fetchConversations')
this.props.fetchConversations(this.state.page)
})
}else{
this.setState({
noMore: true
})
}
}
render() {
const { openArchive } = this.props
return(
<div className="leftPanel">
<div id="subjects-container" className="subjects-wrap">
{(!this.props.isLoading && isEmpty(this.props.conversations)) && <EmptyState style={{marginTop:'40px'}} message={Lang.get('inbox.conversations.emptystate')} />}
{!isEmpty(this.props.conversations) &&
map(this.props.conversations, (conversation, index) => (
<Subject
className={this.props.selectedConversationId === conversation.id ? 'active': '' }
key={index}
index={index}
conversation={conversation}
getConversationMsg={this.props.getConversationMsg}
/>
))
}
<VisibilitySensor scrollCheck={true} active={!this.state.noMore && this.props.conversations.length !== this.props.conversationsTotal} containment={this.container} onChange={this.handleLoaderInview} >
<Loading className="loading-sm" />
</VisibilitySensor>
</div>
{openArchive && <Archive {...this.props} />}
</div>
)
}
}
export default LeftPanel
Hi @kof, thanks for your recent contribution. I had a go at adding this to the example page: http://joshwnj.github.io/react-visibility-sensor/ It's pretty ugly but hopefully gets the functionality across :P
When you have a moment could you please take a look and let me know if this is the way you imagined it being used? Also let me know if you think there is anything else that would be good to demonstrate on the example page.
Thanks!
First of all, sorry for it's not an issue.
and I really appreciate your package.
but I really wonder that it works well at the some old Android browsers. (maybe 4.2~4.4?)
This warning gets printed when you install react-visibility-sensor in a project that uses React 16:
npm WARN [email protected] requires a peer of react@^0.14.0 || ^15.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN [email protected] requires a peer of react-dom@^0.14.0 || ^15.0.0 but none is installed. You must install peer dependencies yourself.
It seems to work fine though
I have some problem with the library when it is working inside the webview
actually our all videos is start playing as the page get rendered and the video is still not in the viewport
and we have enabled the setMediaPlaybackRequiresUserGesture 'true' in the android webview.
My component looks something like this
class MyComponent extends Component {
onChange(isVisible){
this.setState({visCount : this.state.visCount + 1}) //
}
render(){
return <div><VisibilitySensor onChange={(v)=>this.onChange(v)} /></div>
}
}
The issue is onChange
is getting triggered on every render, which then triggers another render so visCount keep auto-incrementing. Am I doing something wrong here? How are you supposed to use setState in onChange?
I had a bug which after some investigation I tracked down to the following in visibility-sensor.js
var el = ReactDOM.findDOMNode(this);
For me this was selecting the child span in the sensor rather than the actual sensor element. This was throwing off the results from getBoundingClientRect on the next line.
I'm not familiar enough with React yet to know why the child node is being returned instead of the component node. I've read up on it and my understanding is that it should return the parent.
For now I've edited the line to be:
var el = ReactDOM.findDOMNode(this).parentNode;
Which selects the correct element. But I would like to know why. Does anyone have any insight.
Could it be related to the sensor elements being absolutely positioned?
Now that we've got quite a few different options it would be great to make examples that cover all the bases.
Maybe a bunch of jsbin / codepen examples showing different combinations of options.
Maybe even an interactive demo where you can adjust / switch props and see how it changes behaviour.
I have a need to reset isVisible value manually via props. The reason I need it is because I am using active: false
option, which means auto check of visibility is disabled. The logical consequence of that is an element doesn't get a signal when its hidden and once it becomes visible again it "thinks" it is already visible and doesn't triggers the onChange callback.
I will send you a pull request in a bit.
I think a good addition would be to add an offset to partialVisibility (required px from different sides in order for the element to be visible or not) it would be very useful for elements that are almost as big as the viewport.
Same as how the offset from jquery appear plugin works. I can try to implement and send a PR but after #41 is merged.
I had some problems with React 0.13 so I wrote a new class.
(Sorry but wrote new was faster than debug)
Maybe this will help someone: https://gist.github.com/B-Stefan/ef800cd0f8b10597d5df
Scenario:
const Foo = (props) => (
{props.greet ? <div>Hello, there.</div> : null}
)
<VisibilitySensor>
<Foo greet={false} />
</VisibilitySensor>
In this case, VisibilitySensor breaks with the following error:
Uncaught TypeError: Cannot read property 'getBoundingClientRect' of null
So that I can use VisibilitySensor as a container for the contents instead of having additional element just for the position/size measurements.
Also right now even if I want to put this as a separate element, it needs to get the same width and hight as the element I want to be notified about viewport. So it needs to support width/height or className over props anyways.
The getBoundingClientRect
API takes several ms as it forces the web engine to recalculate the layout (see https://gist.github.com/paulirish/5d52fb081b3570c81e3a). This is an issue for the visibility-sensor component if we apply that to thousands of elements.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.