I'm looking to recreate this elastic nav in a React component (see Codepen). Right now it's written in jQuery--and I'm having trouble getting the position and width in React. Would using React refs be a good way to access that information?
Any thoughts or insight would be greatly appreciated. I've been trying to figure out how to recreate this all week.
The click handler was pretty simple in React. I just created a function and then used onClick. And setting the active class wasn't too hard either. I ended up using a prop.
But these two sections of the code are where I'm stuck--and unsure how to recreate in React:
var activeItem = tabs.find('.active');
var activeWidth = activeItem.innerWidth();
$(".selector").css({
"left": activeItem.position.left + "px",
"width": activeWidth + "px"
});
var activeWidth = $(this).innerWidth();
var itemPos = $(this).position();
$(".selector").css({
"left":itemPos.left + "px",
"width": activeWidth + "px"
});
Here's the full JavaScript/jQuery:
var tabs = $('.tabs');
var selector = $('.tabs').find('a').length;
var activeItem = tabs.find('.active');
var activeWidth = activeItem.innerWidth();
$(".selector").css({
"left": activeItem.position.left + "px",
"width": activeWidth + "px"
});
$(".tabs").on("click","a",function(e){
e.preventDefault();
$('.tabs a').removeClass("active");
$(this).addClass('active');
var activeWidth = $(this).innerWidth();
var itemPos = $(this).position();
$(".selector").css({
"left":itemPos.left + "px",
"width": activeWidth + "px"
});
});
And here's the full HTML:
<div class="wrapper">
<nav class="tabs">
<div class="selector"></div>
</i>Popular
Upcoming
</i>My Movies
</i>Search
</nav>
</div>
And here's the link to the project with CSS (if you want to see it in action).
I'd love for this to be compartmentalized in a single component if possible. But if it needs to have some sub-components that's okay as well.
Thank you!
You can absolutely make this work in react, and you are on the right track.
You can use this syntax to create references to an element in React:
<input ref={input => this.myInput = input} />
Which can then be accessed by a handler tied to your tab links. For example:
handleTabClick = (e, index) => {
e.preventDefault()
position = {left: this.myInput.offsetLeft, top: this.myInput.offsetTop} // Make sure this is the correct JS!
width = this.myInput.offsetWidth // Make sure this is the correct JS!
}
render () {
return (
...
<a href="#" class="tab" onClick={e => handleClick(e, 1)}>Tab 1</a>
...
)
}
I'm not 100% sure about the element properties being accurate (width, position), but you can do a quick google/search on stackoverflow to find the correct ways to do it in vanilla JS instead of jQuery. You can also use this site for native JS methods which accomplish the same as jQuery: http://youmightnotneedjquery.com/
Related
I have a list of image names in an array, but I want to prefix them with a host and add some fun sizing options for responsive. Here is what I have:
new Vue({
el: '#gallery_images',
data: {
desktop: 'w=400&h=400&fit=crop&crop=faces,entropy&auto=format,compress 400w,',
ipad: 'w=600&h=600&fit=crop&crop=faces,entropy&auto=format,compress 600w,',
mobile: 'w=900&h=900&fit=crop&crop=faces,entropy&auto=format,compress 900w,',
responsive: 'w=1200&h=1200&fit=crop&crop=faces,entropy&auto=format,compress 1200w',
host: 'https://tom.imgix.net/artsy/',
images: [ '1.jpg?', '2.jpg?', '3.jpg?', '4.jpg?', '5.jpg?', '6.jpg?']
}
});
I am able to do some clumsy v-bind in the html and this works:
<div id="gallery_images">
<img v-for="image, in images" :src="'https://tom.imgix.net/artsy/' + image" v-bind:srcset="host + image + desktop + host + image + ipad + host + image + mobile + host + image + responsive" sizes="(min-width: 1240px) 33vw, 100vw"/>
</div>
[Codepen]https://codepen.io/daletom/pen/WNvNgOa
This actually works! But I think there is a better way. Instead of having to write all this host+image+size+host+image+size on and on. It would be nice if I could just do a function for that and use it on all my pages in my website easily.
So I tried to build this. I was thinking maybe add a computed function:
computed: {
vueSDK: function () {
return this.host + this.images + this.desktop + this.host + this.images + this.ipad + this.host + this.images + this.mobile + this.host + this.images + this.responsive
}
}
Then just pass the function in the srcset:
<div id="gallery_images">
<img v-for="image, in images" :src="host + image" v-bind:srcset="vueSDK" sizes="(min-width: 1240px) 33vw, 100vw"/>
</div>
But that ain't working. It's just returning the same first image over and over. You can see in this [codepen]https://codepen.io/daletom/pen/QWbJKPp
Is there an eloquent way to pass a function in the srcset to dynamically load in all these images with the various responsive sizing?
The problem is, that you are only defining one single computed property per component. The v-for though requires a computed property for each iteration. The easiest way to do that is to use a method instead of a computed variable (with the drawback of losing the cache of a computed variable). If you looped through child components, you could also have a computed variable in each of those child components. I would prefer the second option. Here are examples of these two options:
Using a method
<template>
<img v-for="image in images" :src="host + image" :srcset="getSrcset(image)" sizes="(min-width: 1240px) 33vw, 100vw" />
</template>
<script>
export default {
data () {
return {
host: ...,
images: ...
}
},
methods: {
getSrcset () {
return ...;
}
}
};
Using a child component
In the parent component:
<custom-image v-for="image in images" :image="image" />
And the child component:
<template>
<img :src="host + image" :srcset="srcset" sizes="(min-width: 1240px) 33vw, 100vw" />
</template>
<script>
export default {
props: 'image',
data () {
return {
host: ...
}
},
computed: {
srcset () {
return ...
}
}
};
</script>
Looking for the best way of implementing a custom hover with plotly.js along with react. The following is in the plotly.js docs https://plot.ly/javascript/hover-events/
var myPlot = document.getElementById('myDiv'),
hoverInfo = document.getElementById('hoverinfo'),
d3 = Plotly.d3,
N = 16,
x = d3.range(N),
y1 = d3.range(N).map( d3.random.normal() ),
y2 = d3.range(N).map( d3.random.normal() ),
data = [ { x:x, y:y1, type:'scatter', name:'Trial 1',
mode:'markers', marker:{size:16} },
{ x:x, y:y2, type:'scatter', name:'Trial 2',
mode:'markers', marker:{size:16} } ];
layout = {
hovermode:'closest',
title:'Hover on Points'
};
Plotly.plot('myDiv', data, layout);
myPlot.on('plotly_hover', function(data){
var infotext = data.points.map(function(d){
return (d.data.name+': x= '+d.x+', y= '+d.y.toPrecision(3));
});
hoverInfo.innerHTML = infotext.join('
');
})
.on('plotly_unhover', function(data){
hoverInfo.innerHTML = '';
});
But when using react I'm not sure how to get a reference to the #hoverInfo div. Any suggestions?
You can use ref
render() {
<div id='hoverInfo' ref={ (el) => this.hoverInfo = el }>
</div>
}
will give you a reference in your component to the hoverInfo div which can be used with a library like plotly.
I am facing the same problem and found the easiest way to customize hovers is using the hovertempleate property, if you only want to customized the text: https://plotly.com/javascript/hover-text-and-formatting/
Another option is playing with onHover and onUnhover props in the layout: https://github.com/plotly/react-plotly.js/ You can define an state hover which turns true when hovered and false when unhovered. With hover true you could make your component to appear with the information you need.
Hi Guys My requirement is to make an element draggable across the screen and set the position of the element to the place where users stops dragging. So far I am able to make a element draggable across the screen but once released it is going back to its old position (position where it was earlier) I am using ngDraggable directive of angular. Sorry I am new to Ionic and angular. Any help will be highly appreciated.
My code goes as follows
<div ng-drag="true" id="draggableAxis" ng-style="{{draggedStyle}}" ng-drag-success="onDragComplete($data,$event)">
<img src="img/axis.png" >
<img ng-src="img/other.png" style="width:75px">
</div>
In my controller:
$scope.draggedStyle = {top: '96px',
right: '90px'};
var onDraggableEvent = function(evt, data) {
if(evt.name=='draggable:start'){
console.log('draggable-start');
console.log(data.x);
console.log(data.y);
}else{
console.log('draggable-end');
console.log(data);
console.log(data.element.centerX +' '+data.element.centerY);
console.log(evt);
$scope.setPosition(data);
}
}
$scope.onDragComplete=function(data,evt){
console.log("drag success, data:", data);
console.log(evt);
}// this fn doesnot gets triggered
$scope.$on('draggable:start', onDraggableEvent);
$scope.$on('draggable:end', onDraggableEvent);
$scope.setPosition=function(data){
$scope.draggedStyle = {
top: data.x+'px',
right: data.y+'px'
};
}
SCREEN SHOT OF MOBILE VIEW
I updated the fix to https://plnkr.co/edit/fVaIUVvAd7jLETkwVepm?p=preview
One of the problems was
ng-style="{{draggedStyle}}"
which should be
ng-style="draggedStyle"
Also, I switched the setPosition method to flip the x and y and used left because x indicates position from the left and not right.
$scope.setPosition = function(data) {
$scope.draggedStyle = {
top: data.y + 'px',
left: data.x + 'px'
};
}
Hope that helps
I am attempting to write a <Heading /> component that will abstract away some of the headaches of dealing with headings (h1, h2, etc) in conjunction with accessibility standards.
The goal being that this component would be able to dynamically choose a h(1,2,3) depending on its closest parent heading. However, Looking up the DOM tree like this remind me more of jQUERY than react. I've looked through the docs and SO but haven't seen anything about it, so I'm not sure if this is even possible.
So the question is: Is it possible for a component to know where it is rendered in the DOM tree and then execute some logic with that info? (probably somewhere in componentWillMount).
You could look upward in the DOM in componentDidMount and componentDidUpdate, but that's messy and uncontrolable. There may be a better solution but this is the first thing that comes to mind.
var headerFactories = ['h1', 'h2', 'h3', 'h4'].map(function(tag){
return React.createFactory(tag)
});
var makeHeader = function(level){
var make = function(){
var factory = headerFactories[make.level]
|| headerFactories[headerFactories.length - 1];
return factory.apply(this, arguments);
};
make.sub = function(){
return makeHeader(make.level + 1);
};
make.level = level;
return make;
}
The api may seem a bit strange, but let's say we have a Page component and an Article child which may have another Article child.
var Page = React.createClass({
render: function(){
// make a root level header
var header = makeHeader();
return <div>
{header(null, "My Page")}
<Article headerFactory={header.sub()} data={foo} subArticle={bar} />
</div>
}
});
var Article = React.createClass({
render: function(){
var subArticle = false;
if (this.props.subArticle) {
subArticle = <Article headerFactory={this.props.headerFactory.sub()} />
}
return <div>
{this.props.headerFactory(null, this.props.data.title)}
</div>
}
});
What happens is when we do headerFactory(null, "foo") we get a component of that header level. When we call .sub() we get a version of that headerFactory with the next header level.
var h1 = makeHeader();
var h2 = h1.sub();
var h3 = h2.sub();
var h4 = h3.sub();
This allows components to have header levels based on their parents without breaking out of React.
I've just started learning AngularJS and I'm approaching directives. I'd need to create a directive named f.e. "thumbnail" which takes as input a "src" (the image) and create a thumbnail of it.
So far I've coded the javascript function to create the thumbnail which does its job correctly:
function readURL(input) {
var imageSize = 50;
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
var blah = $('#myimage');
blah.attr('src', e.target.result);
var height = blah.height();
var width = blah.width();
if (height < imageSize && width < imageSize) {
if (width > height) {
blah.width(imageSize);
}
else {
blah.height(imageSize)
}
}
};
reader.readAsDataURL(input.files[0]);
}
}
</script>
<body onload="readURL(this);">
<img id="myimage" src="../picture.gif" width="50" alt="your image" />
However I have not been able to find a simple example which produces (in AngularJS) an Element, based on a JS function. Any help ?
Thanks!
It depends on what you want to achieve. If your purpose of creating a thumbnail at client-side is to:
Upload in a smaller size in a web application: perhaps you can make use of a library like http://www.plupload.com.
Display only: you should just use CSS resizing with width and height properties.
<img src="/original/320x320.jpg" style="width: 100px; height: 100px">
Reading/storing in an Ionic/PhoneGap app: try make use of angular-thumbnail (disclaimer: I wrote it)
Doing image manipulation of this kind on the client side is not a very good idea , because, as of now there isn't a good support for Filereader in IE.
You can delegate this either to server(you will find lot of node module for image manipulation) or you can use external services like cloudinay