I cannot make IScroll to work with React.js.
var SubHeaderMenu = React.createClass({
getDefaultProps: function () {
return {items: []};
},
componentDidMount: function () {
if (this.props.items.length && this.isMounted()) {
this.scroll = new IScroll(this.getDOMNode(), {
scrollX: true,
scrollY: false,
mouseWheel: true,
});
}
},
render: function () {
var itemNodes = this.props.items.map(function (item) {
return <div key={item.name} className="subheader-item">{item.name}</div>;
}.bind(this));
return (
<div className="bar bar-standard bar-header-secondary subheader">
<div className="scroller" ref="scroller">
<div className="scroller-inner" ref="scrollerInner">{itemNodes}</div>
</div>
</div>
);
}
});
SubHeaderMenu component receive list of items as props. This works fine if I set up width for .scroller in CSS:
.scroller { width: 600px; ... }
But without width in CSS it doesn't scroll..
How to handle dynamic width of every list item?
There is a npm-package for react and IScroll:
https://www.npmjs.com/package/react-iscroll
Related
I've got some questions about vuejs and router ..
window.addEventListener('scroll', ...) also is not detected in my component.
When I typed 'window.scrollY' in console.log. It will always return 0 back to me.
Right scroll(Y) are available and window.innerHeight is not equal 0
I can't detected when client move scroll to bottom
I use vuestic and vue-router
Thank you
created () {
// Not working because window.scrollY always return 0
window.addEventListener('scroll', this.handleScroll);
},
methods: {
handleScroll (event) {}
}
you can try to listen for scrolling a child element.
and using getBoundClientRect:
<template>
<div id="app">
<nav>navbar</nav>
<main id="listen">main</main>
</div>
</template>
<script>
export default {
name: "App",
created() {
document.addEventListener("scroll", this.listenScroll);
},
destroyed() { // remember to remove the listener when destroy the components
document.removeEventListener("scroll", this.listenScroll);
},
methods: {
listenScroll() {
let myScroll = document.querySelector("#listen").getBoundingClientRect()
.top;
console.log(myScroll);
},
},
};
</script>
<style>
nav {
height: 100px;
}
main {
height: 700px;
}
</style>
here there is a codesandbox https://codesandbox.io/s/great-hill-x3wb1?file=/src/App.vue:0-560
I am very new to vue.js and fumbling my way though it, forgive me if my terms are incorrect. I am creating a touchscreen application that needs to be ADA compliant (only the bottom part of the screen is accessible, so i have to use buttons for interaction).
I have a parent component with a carousel creating an array of slides, pulling data from my child component.
parent component HTML
<carousel :navigateTo="selectedListIndex" #pageChange="OnPageChange">
<slide v-for="(member, index) in selectedList" :key="index">
<MemberBioPage :member="member"/>
</slide>
</carousel>
parent component SCRIPT:
export default {
data () {
return {
currentPage: 0
}
},
components: {
MemberBioPage,
Carousel,
Slide
},
computed: {
selectedList () {
return this.$store.state.selectedList
},
selectedListIndex () {
return this.$store.state.selectedListIndex
}
},
methods: {
OnPageChange (newPageIndex) {
console.log(newPageIndex)
this.currentPage = newPageIndex
}
}
}
within my child component, i have bio copy being pulled from my data and arrow buttons that allow you to scroll the text. There is an outer container and an inner container to allow the scrolling and based on the height that the content takes up in the container will determine when the arrows disable or not.
child component HTML:
<div class="member-bio-page">
<div class="bio">
<div class="portrait-image">
<img :src="member.imgSrc" />
</div>
<div class="bio-container">
<div class="inner-scroll" v-bind:style="{top: scrollVar + 'px'}">
<h1>{{ member.name }}</h1>
<div class="description-container">
<div class="para">
<p v-html="member.shortBio"></p>
</div>
</div>
</div>
</div>
<div class="scroll-buttons">
<div>
<!-- set the class of active is the scroll variable is less than 0-->
<img class="btn-scroll" v-bind:class="{ 'active': scrollVar < 0 }" #click="scrollUp" src="#/assets/arrow-up.png">
</div>
<div>
<!-- set the class of active is the scroll variable is greater than the height of the scrollable inner container-->
<img class="btn-scroll" v-bind:class="{ 'active': scrollVar > newHeight }" #click="scrollDown" src="#/assets/arrow-down.png">
</div>
</div>
</div>
</div>
child component SCRIPT:
<script>
export default {
props: [
'member', 'currentPage'
],
data () {
return {
scrollVar: 0,
outerHeight: 0,
innerHeight: 0,
newHeight: -10
}
},
mounted () {
this.outerHeight = document.getElementsByClassName('bio-container')[0].clientHeight
this.innerHeight = document.getElementsByClassName('inner-scroll')[0].clientHeight
this.newHeight = this.outerHeight - this.innerHeight
return this.newHeight
},
methods: {
scrollUp () {
console.log(this.scrollVar)
this.scrollVar += 40
},
scrollDown () {
console.log(this.scrollVar)
this.scrollVar -= 40
},
showVideo () {
this.$emit('showContent')
}
}
}
</script>
I am able to get the height of the first bio i look at, but on page change it keeps that set height. I basically want the code in mounted to be able to rerun based on the index of the slide i am on. I need 'newHeight' to update on each page change. I tried grabbing the 'currentPage' from my parent component using props, but it pulls undefined.
here is all a snippet from my data to show you what data i currently have:
{
index: 12,
name: 'Name of Person',
carouselImage: require('#/assets/carousel-images/image.jpg'),
imgSrc: require('#/assets/bio-page-image-placeholder.jpg'),
shortBio: '<p>a bunch of text being pulled</p>',
pin: require('#/assets/image-of-pin.png')
}
this is also my store just in case
const store = new Vuex.Store({
state: {
foundersList: founders,
chairmanList: chairmans,
selectedList: founders,
selectedListIndex: -1
},
mutations: {
setSelectedState (state, list) {
state.selectedList = list
},
setSelectedListIndex (state, idx) {
state.selectedListIndex = idx
}
}
})
Alright, so this is a good start. Here's a few things I would try:
Move the code you currently have in mounted to a new method called calculateHeight or something similar.
Call the method from your scrollUp and scrollDown methods.
So your final code would look something like this:
export default {
props: [
'member', 'currentPage'
],
data () {
return {
scrollVar: 0,
outerHeight: 0,
innerHeight: 0,
newHeight: -10
}
},
mounted () {
this.calculateHeight();
},
methods: {
calculateHeight() {
this.outerHeight = document.getElementsByClassName('bio-container')[0].clientHeight
this.innerHeight = document.getElementsByClassName('inner-scroll')[0].clientHeight
this.newHeight = this.outerHeight - this.innerHeight
},
scrollUp () {
console.log(this.scrollVar)
this.scrollVar += 40
this.calculateHeight()
},
scrollDown () {
console.log(this.scrollVar)
this.scrollVar -= 40
this.calculateHeight()
},
showVideo () {
this.$emit('showContent')
}
}
}
I am tring to show todo list with load more option. I am appling limit.Limit is apply to list.But when i add loadmore()function. then i get error this.state.limit is null Wher i am wrong.Any one can suggest me.
here is my code
todoList.jsx
var TodoList=React.createClass({
render:function(){
var {todos}=this.props;
var limit = 5;
function onLoadMore() {
this.setState({
limit: this.state.limit + 5
});
}
var renderTodos=()=>{
return todos.slice(0,this.state.limit).map((todo)=>{
return(
<Todo key={todo.todo_id}{...todo} onToggle={this.props.onToggle}/>
);
});
};
return(
<div>
{renderTodos()}
<a href="#" onClick={this.onLoadMore}>Load</a>
</div>
)
}
});
module.exports=TodoList;
Changes:
1. First define the limit in state variable by using getInitialState method, you didn't define the limit, that's why this.state.limit is null.
2. Define all the functions outside of the render method.
3. Arrow function with renderTodos is not required.
4. Use this keyword to call the renderTodos method like this:
{this.renderTodos()}
Write it like this:
var TodoList=React.createClass({
getInitialState: function(){
return {
limit: 5
}
},
onLoadMore() {
this.setState({
limit: this.state.limit + 5
});
},
renderTodos: function(){
return todos.slice(0,this.state.limit).map((todo)=>{
return(
<Todo key={todo.todo_id}{...todo} onToggle={this.props.onToggle}/>
);
});
};
render:function(){
var {todos} = this.props;
return(
<div>
{this.renderTodos()}
<a href="#" onClick={this.onLoadMore}>Load</a>
</div>
)
}
});
This is witout button click.
As you all know react components has a function componentDidMount() which gets called automatically when the template of that component is rendered into the DOM. And I have used the same function to add the event listener for scroll into our div iScroll.
The scrollTop property of the element will find the scroll position and add it with the clientHeight property.
Next, the if condition will check the addition of these two properties is greater or equal to the scroll-bar height or not. If the condition is true the loadMoreItems function will run.
class Layout extends React.Component {
constructor(props) {
super(props);
this.state = {
items: 10,
loadingState: false
};
}
componentDidMount() {
this.refs.iScroll.addEventListener("scroll", () => {
if (this.refs.iScroll.scrollTop + this.refs.iScroll.clientHeight >=this.refs.iScroll.scrollHeight){
this.loadMoreItems();
}
});
}
displayItems() {
var items = [];
for (var i = 0; i < this.state.items; i++) {
items.push(<li key={i}>Item {i}</li>);
}
return items;
}
loadMoreItems() {
this.setState({ loadingState: true });
setTimeout(() => {
this.setState({ items: this.state.items + 10, loadingState: false });
}, 3000);
}
render() {
return (
<div ref="iScroll" style={{ height: "200px", overflow: "auto" }}>
<ul>
{this.displayItems()}
</ul>
{this.state.loadingState ? <p className="loading"> loading More Items..</p> : ""}
</div>
);
}
}
This is example
This is nav component. This nav component rendered at run time. Text should be visible when mouse will enter to div with ref "text_nav" and text should hide when mouse will leave div with ref "text_nav".
onMouseLeave is not working
var React = require('React');
var $ = require('jquery');
var Nav = React.createClass({
getInitialState: function() {
return {
items: []
}
},
componentWillMount: function() {
var _this = this;
this.serverRequest =
$.post("/nav", {}, function(result) {
_this.setState({
items: result.data
});
})
},
onMouseEnter: function() {
this.refs.text_navigator.style = {display: true}
},
onMouseLeave: function() {
this.refs.text_navigator.style = {display: 'none'}
},
render: function() {
var text = this.state.items.map(function(data, index) {
var icon = "text_" + data.sname;
return (
<div id={icon} key={index} className="text_nav_item">
<p>
<span><a href={data.url}>{data.title} </a></span>
</p>
</div>
);
});
return (
<div id="nav" className="fixed" style={{zIndex: 1018}} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
<div id="text_nav" ref="text_navigator" style={{display: 'none'}} >
<div id="text_nav_content">
{item_text}
</div>
</div>
</div>
)
}
})
First off, the ref is text_nav in your render method, not text_navigator as you're using in onMouseEnter and onMouseLeave. But the main issue is that you can't set the display style the way you're trying to do, as
this.refs.text_navigator.style = {display: true}
The most common approach to take is to set a boolean in state, perhaps called displayTextNavigator. In getInitialState, set it to false, and then your onMouseEnter and onMouseLeave functions can be:
onMouseEnter: function() {
this.setState({ displayTextNavigator: true})
},
onMouseLeave: function() {
this.setState({ displayTextNavigator: false})
},
Now in your render method, you can change your wrapping div to look like this:
<div id="text_nav" ref="text_nav" style={{display: this.state.displayTextNavigator ? 'block': 'none'}} >
You can pull that ternary operator out to earlier in the render method if you'd like it be more readable.
When switching data by selecting slice from an array, putting it into state and rendering by setInterval, I try to create a slider effect by clicking up and down arrows:
UPDATE START
I was finally able to create slider using technique from this codepen:
http://codepen.io/sergiodxa/pen/aOYdeN
UPDATE END
var MainContainer = React.createClass({
getInitialState: function () {
return {
position: 0,
max_elements: 3,
data: [],
source: Array.prototype.slice.call([1, 2, 3, 4, 5, 6, 7]).reverse()
}
},
componentDidMount: function () {
setInterval(this.updateState, 10);
},
arrowUp: function () {
if (this.state.position > 0) {
this.state.position--;
this.updateState();
}
},
arrowDown: function () {
if (this.state.source.length - this.state.position > this.state.max_elements) {
this.state.position++;
this.updateState();
}
},
updateState: function () {
data = this.state.source.slice(this.state.position, this.state.position + this.state.max_elements)
this.setState({data: data});
console.log(this.state.data);
},
render: function () {
var Items = this.state.data.map(function (item, i) {
return (
<div key={i}>
<SimpleItem message={item} active={i == 0 ? true : false}/>
</div>
);
}, this);
return (
<div>
{Items}
<ArrowUp onClick={this.arrowUp}/>
<ArrowDown onClick={this.arrowDown}/>
</div>
);
}
});
var ArrowUp = React.createClass({
render: function () {
return (
<a href="#" className="glyphicon glyphicon-arrow-up" onClick={this.props.onClick}>
</a>
);
}
});
var ArrowDown = React.createClass({
render: function () {
return (
<a href="#" className="glyphicon glyphicon-arrow-down" onClick={this.props.onClick}>
</a>
);
}
});
var SimpleItem = React.createClass({
render: function () {
var className = "well well-lg"
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
className = this.props.active ? className + " active" : className;
return (
<ReactCSSTransitionGroup
transitionName="example"
transitionAppear={true} transitionAppearTimeout={500}
transitionEnter={false} transitionLeave={false}
>
<div className={className}>
{this.props.message}
</div>
</ReactCSSTransitionGroup>
);
}
});
ReactDOM.render(<MainContainer />, document.getElementById('container'));
The animation is only seen when components render for the first time, re-rendered elements won't animate, for some reason.
My styles are:
.example-appear {
opacity: 0.01;
}
.example-appear.example-appear-active {
opacity: 1;
transition: opacity 500ms ease-in;
}
I'm not a frontend guy so any hint or link would be much appreciated.
I noticed a couple of things wrong. This first is in you arrowUp and down functions. State always needs to be set using the setState() function. From the docs:
NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
The second thing is that the children of ReactTransition group must have a unique key. When this key changes that is what tells react transition group to animate.
<ReactCSSTransitionGroup
transitionName="example"
transitionAppear={true} transitionAppearTimeout={500}
transitionEnter={false} transitionLeave={false}
>
{*/ This div needs a key. When the key changes react transition group animates. /*}
<div key={this.state.currentItem} className={className}>
{this.props.message}
</div>
</ReactCSSTransitionGroup>
In the above example I've set the key to this.state.currentItem your up and down arrow functions would update state.currentItem appropriately, react would re-render the component, react transition group would animate because this.state.currentItem changed.