I have an array of divs containing book names that I generate using a .map function on some fetched data from my database. Some of the book names are too long so I have used the following CSS to hide them:
.book-title{
font-weight: 600;
height: 1.5rem;
max-width: 15rem;
overflow: hidden;
padding-top: 0.7rem;
}
But I would like to make the text visible when user hovers over the div. My current solution applies to all divs generated and displayed by the .map function - I am unsure how to make it just apply to divs where the text is too long.
.book-title:hover{
height: 6rem;
overflow: visible;
}
Is there a proper way to do this with react?
here is the relevant .jsx
const listsForFrontPage = listArray.map((book, id) => {
return (
<Link className="book-link" to={`/books/${book.book_id}`} key={id}>
<div className="book-card">
<div className="book-card-image">
<img
className="cover-image"
src={book.cover_image}
alt="The book cover"
/>
</div>
<div className="book-info">
<div className="book-title">{book.title}</div>
<div className="author-name">{book.name}</div>
</div>
</div>
</Link>
)
})
You need to check if the element is overflowing
add useRef:
const refs = useRef(listArray.map(() => React.createRef()));
then you should create a function that checks if the element is overflowing:
const isOverflown = (el) => {
if(!el) return;
const { clientWidth, clientHeight, scrollWidth, scrollHeight } = el;
return scrollHeight > clientHeight || scrollWidth > clientWidth;
}
and then you need to add class based on isOverflown value:
const listsForFrontPage = listArray.map((book, id) => {
return (
<Link className="book-link" to={`/books/${book.book_id}`} key={id}>
<div className="book-card">
<div className="book-card-image">
<img
className="cover-image"
src={book.cover_image}
alt="The book cover"
/>
</div>
<div className="book-info">
<div ref={refs.current[id]} className={`book-title ${isOverflown(refs.current[id].current) ? 'book-title-overflow' : ''}`}>{book.title}</div>
<div className="author-name">{book.name}</div>
</div>
</div>
</Link>
)
})
don't forget to add your css:
.book-title-overflow:hover{
height: 6rem;
overflow: visible;
}
Related
i want to display several elements in cards , coming from my routing system, in a similar way that i am creating my sidebar (that is working properly). i got all titles and icons, and the linking is working perfectly .
This is what i am doing: Since some of my elements, i am getting from my routing system have children (for example, i have spots for "agenda" , "sessao" and "packs e modulos", and the last element, has two elements inside of it "packs" and "modulos") when i am checking my route file, for elements, it wraps each parent, making a wrapper for «agenda», other wrapper for «sessao», and a wrapper with the two children inside it: "packs" e " modulos" (i don't want it's parent to display.
Everything is working as i want except one thing, that is what i am struggling. I display all my cards (wrapped) horizontally, and when a wrapper doensn't fit on the screen it goes to the next line. With the wrappers with only one element works perfectly, however with the wrappers with children, is where i am struggling, since if the wrapper doensn't fit the screen, the entire wrapper changes it's line, and i only want that the child that doesn't fit the line, changes to the next line
For example, if only Agenda, Sessao and Pack fits one line, i want that only Modulo changes to the next line, instead, both Pack and Modulo are chaning.
Here is my code
<template>
<div v-if="!item.hidden&&item.children" class="menu-wrapper">
<template v-if="!item.hidden&&item.children && hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren) && !item.alwaysShow">
<app-link :to="resolvePath(onlyOneChild.path)">
<!-- single -->
<el-card :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest} ">
<!-- <item v-if="onlyOneChild.meta" :title="onlyOneChild.meta.title" /> -->
<!-- <item v-if="onlyOneChild.meta" :icon="onlyOneChild.meta.icon||item.meta.icon" :title="onlyOneChild.meta.title" :collapse="collapse" /> -->
<!-- <span>{{ onlyOneChild.meta.title }}</span> -->
<div class="contentCard">
<div class="icon">
<i :class="onlyOneChild.meta.icon" />
</div>
<span class="title">
{{ generateTitle(onlyOneChild.meta.title) }}
</span>
</div>
</el-card>
</app-link>
</template>
<!-- group -->
<template v-else class="childCard" v-for="child in visibleChildren" :index="resolvePath(item.path)">
<app-link :to="resolvePath(child.path)">
<el-card class="contentClass" :index="resolvePath(child.path)">
<div class="contentCard">
<div class="icon">
<i :class="child.meta.icon" />
</div>
<span class="title">
{{ generateTitle(child.meta.title) }}
</span>
</div>
</el-card>
</app-link>
</template>
</div>
</template>
Here is my css styling
<style lang="css" scoped>
.el-menu--inline span{
font-size: 0.9em;
}
.el-menu-item svg{
margin-right: 11px;
}
.el-menu-item-group__title {
display: none;
}
.is-active .menu-dot{
background: #cca13c;
}
.menu-dot {
width: 21px;
background: #304156;
height: 10px;
border-radius: 20px;
margin-right: 10px;
}
.contentClass{
margin-right:20px;
}
.childCard{
flex-grow:1;
}
.contentCard{
display:grid;
}
.icon{
text-align: center;
font-size: 50px;
}
.title{
text-align: center;
font-size: 10px;
}
.el-card{
margin-right: 20px;
margin-top: 10px;
min-height: 145px;
min-width: 250px;
flex-grow:1;
}
.menu-wrapper{
flex-wrap: wrap;
display: flex;
min-height: fit-content;
}
</style>
Thank You
Edit: since it may be confusing what i said above, to try to be more simple, i have several wrappers with cards inside it, in one line. If i had one card per wrapper, my code would work properly , however, some of my wrappers have 2 or more cards inside, and when it reaches the limit of the screen (especially if is a small screen), the entire wrapper goes to the next line, instead, i only want the card that doensn't have space to fit in the same line, inside that wrapper, to change to the next line.
Solved my problem by doing an workaround (and i post what i did so it may help anyone who had similar problem as me), and simply ungroup my children. I was using a routes method, that simply was retunirng my $store.state.permission.routes, and doing all the magic on my template. I changed my approach and did all the magic on the routes method, and then simpply passing an object array with my ungrouped components, like this:
routes() {
this.rotas = this.$store.state.permission.routes;
console.log(this.$store.state.permission.routes)
var k=0;
for(let i in this.rotas){
if( !this.rotas[i].hidden&&this.rotas[i].children){
if(this.hasOneShowingChild(this.rotas[i].children,this.rotas[i]) && (!this.onlyOneChild.children||this.onlyOneChild.noShowingChildren) && !this.rotas[i].alwaysShow){
this.result[k]= this.rotas[i];
this.result[k].meta=this.rotas[i].children[0].meta;
k++;
}
else{
var childrens = this.rotas[i].children.filter(item => !item.hidden);
for(let child in childrens){
this.result[k]= childrens[child];
k++;
}
}
}
}
return this.result;
},
and my template now
<template>
<div class="menu-wrapper">
<app-link :to="resolvePath(item.path)">
<!-- single -->
<el-card :index="resolvePath(item.path)" :class="{'submenu-title-noDropdown':!isNest} ">
<!-- <item v-if="onlyOneChild.meta" :title="onlyOneChild.meta.title" /> -->
<!-- <item v-if="onlyOneChild.meta" :icon="onlyOneChild.meta.icon||item.meta.icon" :title="onlyOneChild.meta.title" :collapse="collapse" /> -->
<!-- <span>{{ onlyOneChild.meta.title }}</span> -->
<div class="contentCard">
<div class="icon">
<i :class="item.meta.icon" />
</div>
<span class="title">
{{ generateTitle(item.meta.title) }}
</span>
</div>
</el-card>
</app-link>
</div>
</template>
I am using react in an application.
I am trying to show cards (not by using bootstrap or other CSS Library). I using my own CSS to show Cards.
Now I want to show a left and Right Arrow on Each Card when each card 'll have some images related to it.
When user hover the mouse on each card and if there are some images then card 'll show left and right arrows and then user can click on left and right arrow to see more images.
I am using this example for this purpose and trying to implement though ReactJs.
https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_slideshow
Please see tried code below
------------------------------------------card.js-------------------------------
import React, { Component } from 'react';
import ReactDOM from "react-dom";
import Button from '../Button';
import Stats from '../Stats';
import { loadData } from '../../actions';
import './examplecard.css';
class ExampleCard extends Component {
componentDidMount() {
this.props.loadData();
}
componentWillUnmount(){
}
render() {
var slideIndex = 1;
showDivs(slideIndex);
function plusDivs(n) {
showDivs(slideIndex += n);
}
function showDivs(/*n*/) {
//var i;
return
(<div className="w3-content w3-display-container">
{/* var x = document.getElementsByClassName("mySlides"); */}
{/* if (n > x.length) {slideIndex = 1} */}
{/* var slideIndex = 1; */}
{/* if (n < 1) {slideIndex = x.length} */}
{/* for (i = 0; i < x.length; i++) { */}
{/* x[i].style.display = "none"; */}
<img className="mySlides imageHide" src="img_snowtops.jpg" />
<img className="mySlides imageHide" src="img_lights.jpg" />
<img className="mySlides imageHide" src="img_mountains.jpg" />
<img className="mySlides imageHide" src="img_forest.jpg" />
{/* } */}
{/* x[slideIndex-1].style.display = "block"; */}
<img className="mySlides imageShow" src={"img_forest.jpg"} />
<button className="w3-button w3-black w3-display-left" onClick={() => plusDivs(-1)}>❮</button>
<button className="w3-button w3-black w3-display-right" onClick={() =>plusDivs(1)}>❯</button>
</div>)
}
return (
<div>
<div className="row">
<section className="">
{data.map(image => (
<div
key={image.id}
className="c"
>
<Stats stats={imageStats[image.id]} />
<div className="cad">
<showDivs />
<img className="none"
src={image.urls.small}
alt={image.user.username}
/>
<span>Description</span>
{/* <div class="w3-content w3-display-container">
<img class="mySlides" src="img_snowtops.jpg" style="width:100%" />
<img class="mySlides" src="img_lights.jpg" style="width:100%" />
<img class="mySlides" src="img_mountains.jpg" style="width:100%" />
<img class="mySlides" src="img_forest.jpg" style="width:100%" />
<button class="w3-button w3-black w3-display-left" onclick="plusDivs(-1)">❮</button>
<button class="w3-button w3-black w3-display-right" onclick="plusDivs(1)">❯</button>
</div> */}
</div>
</div>
))}
</section>
</div>
</div>
</div>
);
}
}
-------------------------examplecard.css-----------------------------------
.c {
float: left;
width: 20%;
padding: 0 10px;
}
.c {
float: left;
width: 20%;
padding: 0 10px;
}
.row {margin: 0 -5px;}
cad {
padding: 6px;
max-width: 100%;
margin: 0 auto;
}
.w3-content,.w3-auto{margin-left:auto;margin-right:auto}.w3-content{max-width:980px}.w3-auto{max-width:1140px}
.w3-display-container{position:relative}.w3-tooltip .w3-text{display:none}.w3-tooltip:hover .w3-text{display:inline-block}
.w3-display-container:hover .w3-display-hover{display:block}.w3-display-container:hover span.w3-display-hover{display:inline-block}.w3-display-hover{display:none}
.mySlides {display:none;}
.w3-button{border:none;display:inline-block;padding:8px 16px;vertical-align:middle;overflow:hidden;text-decoration:none;color:inherit;background-color:inherit;text-align:center;cursor:pointer;white-space:nowrap}
.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
.w3-button:disabled{cursor:not-allowed;opacity:0.3}.w3-disabled *,:disabled *{pointer-events:none}
.w3-button:first-child,.w3-dropdown-click:hover > .w3-button:first-child{background-color:#ccc;color:#000}
.w3-button,.w3-bar-block .w3-dropdown-click .w3-button{width:100%;text-align:left;padding:8px 16px}
.w3-button{white-space:normal}
.w3-button,.w3-dropdown-click.w3-mobile,.w3-dropdown-click.w3-mobile .w3-btn,.w3-dropdown-click.w3-mobile .w3-button{width:100%}
.w3-button:hover{color:#000!important;background-color:#ccc!important}
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
.w3-display-left{position:absolute;top:50%;left:0%;transform:translate(0%,-50%);-ms-transform:translate(-0%,-50%)}
.w3-display-right{position:absolute;top:50%;right:0%;transform:translate(0%,-50%);-ms-transform:translate(0%,-50%)}
.imageHide
{
width:'100%';display:'none';
}
.imageShow
{
display:'block';
}
I am trying to implement the same code as in w3schools but found some issues.
What should I do to fix in code?
Application built with
{
"react": "16.13.0",
"react-dom": "^16.13.0",
"react-redux": "^7.2.0",
"redux": "^4.0.4"
"#material-ui/core": "^4.9.5"
}
I am facing these error message .
./src/components/Cards/ExampleCard.js
Line 43:17: Expected an assignment or function call and instead saw an expression no-unused-expressions
Search for the keywords to learn more about each error.
I'm currently working on a dating website for a school project, but i'm stuck and not sure if i'm on the right way.
On the user profile, i want a list of the photos the user choose to show, and i want a hover on the photo where the pointer is.
In my state i added a listPhotoHover: [ ], a tab that contains variables true or false. listPhotoHover[0] = true means the first photo of the list has a hover, false means no hover.
I map and add a div for every photo with a onMouseEnter( ) that takes the photo index and set it an hover if fired.
The hover appears if listPhotoHover[index] exists, the hover div has an onMouseLeave( ) that takes the photo index and set the hover of the photo as false.
Everything seems to work but i'm not sure if it's the best way to do it, and when i move very fast on every photo the hover is still there i think the onMouseLeave( ) don't run.
Here's my code
Map of every photo :
photos.map((photo, index) => {
return
<div className={`flex row`} key={index} >
<img
className={classes.userPhotos}
src={photo}
alt={`photo de ${name}`}
onMouseEnter={() => { this.haveHover(index) }}
/>
{
listPhotoHover[index]
? <div
className={`
absolute flex center alignCenter
${classes.userPhotos} ${classes.photoHover}
`}
onMouseLeave={
() => { this.removeHover(index) }
}
/>
: null
}
</div>
})
function when onMouseEnter() or onMouseLeave() is fired:
haveHover (index, value) {
const tab = this.state.listPhotoHover
tab[index] = value
this.setState({listPhotoHover: tab})
}
I would like to know why does the onMouseLeave() don't work when my pointer move very fast and also what is the best way to do an hover on a map.
Thank you for your advices and sorry if i don't write english correctly
ps: i checked previous questions and didn't find any answer yet :(
Josephine
I don't believe you need javascript to achieve your desired effect. If it is simply to show something when the image is hovered, you can uses some advanced CSS selectors to do this. Run the snippet below to
html {
font-family: sans-serif;
font-size: 10px;
}
.wrap {
position: relative;
}
.image {
width: 80px;
height: 80px;
}
.imageinfo {
display: none;
width: 80px;
height: 17px;
background: #cc0000;
color: #efefef;
padding: 3px;
text-align: center;
box-sizing: border-box;
position: absolute;
bottom: -13px;
}
/* here's the magic */
.image:hover+.imageinfo {
display: block;
}
Hover the image to see the effect
<div class="wrap">
<img class="image" src="https://www.fillmurray.com/80/80" />
<div class="imageinfo">
Bill Murray FTW
</div>
</div>
I just realized i did it all wrong, i added a className with a '&:hover' on the div containing one photo, and it works. No need javascript :D
I don't know why i wanted to complicate it haha..
className:
ANSWER: {
'&:hover': {
opacity: '0.4',
cursor: 'pointer'
}
}
div with the photo:
photos.map((photo, index) => {
return <div className={`flex row ${classes.ANSWER}`} key={index} >
<img
className={classes.userPhotos}
src={photo}
alt={`${name}`}
/>
</div>
})
Hope my mistake will help some people
Im trying to reuse a component i VueJs. This is the first time Im using Vue.
I have a couple of components in app.vue. And one of them I want to use two times with the only differens between them is the text and the background-image.
I can't wrap my head around how Im gonna make that work. Any Ideas???
App.vue
<!-- first contact banner -->
<lrContactBanner bannerText="The text to the first component"></lrContactBanner>
<!-- second contact banner -->
<lrContactBanner bannerText="text text" backgroundImage="contact.jpeg"></lrContactBanner>
ContactBanner.vue
<div class="banner parallax overlay" :style="{ 'background-image': backgroundImage }">
<div class="container section">
<div class="columns">
<div class="column">
<h3> {{ bannerText }} </h3>
</div>
</div>
</div>
</div>
<script>
export default {
name: 'lrContactBanner',
props: ['bannerText', 'backgroundImage']
}
</script>
<style>
.parallax {
/* background-image: url("../img/lrImage.jpg"); */
height: 250px;
/* Create the parallax scrolling effect */
background-attachment: fixed;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
position: relative;
}
</style>
You need to use bound props with v-bind:bannerText or short-hand :bannerText to pass the values to the Banner component. You also need to use quoted single quotes to pass a string to the component since it's looking for an object by default. Here's a working example.
<lrContactBanner :bannerText="'text text'" :backgroundImage="'contact.jpeg'"></lrContactBanner>
For the background-image I would actually use a computed property like
<template>
<div class="banner parallax overlay" :style="style">
<h3>{{ bannerText }} </h3>
</div>
</template>
<script>
export default {
props: ['bannerText', 'backgroundImage', 'color'],
computed: {
style() {
return 'background-image: url(' + this.backgroundImage + ')';
}
}
}
</script>
I am trying to change the page's background image depending on the image being hovered.
This is the layout of the page:
HTML:
<div id="main">
<img id="img1" src="1.jpg" />
<img id="img2" src="2.jpg" />
</div>
CSS:
#img1:hover #main
{
background: url('images/1.jpg'); /* not working */
}
#img2:hover #main
{
background: url('images/2.jpg'); /* not working */
}
'#main' is the ID I set for the tag.
Any ideas?
If you need change the background-image of #main div you should use CSS and jQuery:
http://jsfiddle.net/Soldier/cyAXv/1/
HTML
<body>
<div id="main">
<h1>Hi!</h1>
<img id="img1" src="img1"/>
<img id="img2" src="img2"/>
</div>
</body>
JS
$('#img1').hover(function() {
$('#main').css("background","url('background1')");
})
$('#img2').hover(function() {
$('#main').css("background","url('background2')");
})
You can't traverse backwards in CSS selectors. That is to say, you can't apply a style to an ancestor/parent based on the state of a child/descendant.
You will need to use JavaScript unfortunately. You can use classes and define the styles in CSS though to make it less lame. Something like this:
jsFiddle
HTML
<div id="main">
<div id="img1"></div>
<div id="img2"></div>
</div>
CSS
#main.img1 {
background: url('https://www.google.com.au/images/srpr/logo4w.png');
}
#main.img2 {
background: url('https://www.google.com.au/images/srpr/logo4w.png');
}
#img1,
#img2 {
width:100px;
height:100px;
background-color:#F00;
margin:10px;
}
JavaScript
var main = document.getElementById('main'),
img1 = document.getElementById('img1'),
img2 = document.getElementById('img2');
img1.onmouseover = function () {
main.className = 'img1';
};
img2.onmouseover = function () {
main.className = 'img2';
};
img1.onmouseout = function () {
main.className = '';
};
img2.onmouseout = function () {
main.className = '';
};