ReactJS — How to modify CSS properties using createRef()? - css

I am trying to add vertical movement to a list of stacked titles time I change page changes in FullPageJS. I get no error at all, but the action is not happing. My guess is that I am wrong trying to modify the CSS property transition using the element ref. Can anyone spot something wrong with the code below?
Thank you in advance.
React
componentDidMount() {
titleWidth = this.title.current.clientHeight
titleHeight = this.title.current.clientHeight
this.mask.current.css = {
height: titleHeight + "px",
width: titleWidth + "px",
}
this.frame.current.css = { top: titleHeight + "px" }
}
onLeave(origin, destination, direction) {
console.log("Leaving section " + origin.index)
// Actions not working
if (direction == "down") {
this.frame.current.style.transition = { top: "-=" + titleHeight }
} else if (direction == "up") {
this.frame.current.style.transition = { top: "+=" + titleHeight }
}
}
HTML
<div id="wrapper--frame" className="flex justify-center">
<div ref={this.mask} id="mask" className="w-5/6 overflow-hidden">
<div ref={this.frame} id="frame">
<div ref={this.title} className="text-p frame--title ">
One
</div>
<div ref={this.title} className="text-p frame--title ">
Two
</div>
<div ref={this.title} className="text-p frame--title ">
Three
</div>
<div ref={this.title} className="text-p frame--title ">
Four
</div>
</div>
</div>
</div>

Related

Make text overflow visible on hover using react, CSS

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;
}

Hide parent div if two inner divs are empty

Lots of examples can be found to hide a parent div when ONE inner div is empty, but in my case I need to hide the parent div if TWO inner divs are empty:
<div class="parent">
<div class="child1"></div>
<div class="child2"></div>
</div>
Background: I am using Angular with ng-content select to fill the child divs with content. Sometimes none of these templates are used thus both child divs will be empty.
<div class="parent">
<div class="child1"><ng-content select="[child1]"></ng-content></div>
<div class="child2"><ng-content select="[child2]"></ng-content></div>
</div>
Sorry I run out of time, but would be a shame to not share what I was trying to make. Maybe not perfect because of the time but hopefully you get the idea. PS using jQuery.
$(".parent").each(function() {
var empty1 = 0;
var empty2 = 0;
var who = $(this);
$(this).find(".check").each(function() {
var check = $(this).html();
if(check == '<ng-content select="[child1]"></ng-content>') {
var empty1 = 1;
}
if(check == '<ng-content select="[child2]"></ng-content>') {
var empty2 = 1;
}
if(empty1 == 1 && empty2 == 1) {
$(who).slideUp(100);
}
});
});
.parent {
height:10vh;
background:#F00;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="parent">
<div class="check child1"><ng-content select="[child1]"></ng-content></div>
<div class="check child2"><ng-content select="[child2]"></ng-content></div>
</div>
<div class="parent">
<div class="check child1"><ng-content select="[child1]">a</ng-content></div>
<div class="check child2"><ng-content select="[child2]"></ng-content></div>
</div>
I solved it using Angular ViewChild:
<div class="column column-3" [class.hidden]="!hasContentInColumn3">
<div class="line-1" #c3a>
<ng-content select="[c3a]"></ng-content>
</div>
<div class="line-2" #c3b>
<ng-content select="[c3b]"></ng-content>
</div>
</div>
export class ListItemComponent implements OnInit {
public hasContentInColumn3 = false;
#ViewChild('c3a', { static: true }) c3a: ElementRef;
#ViewChild('c3b', { static: true }) c3b: ElementRef;
ngOnInit () {
// Hide div.column-3 when line-1 and line-2 are not provided
this.hasContentInColumn3 = (
(this.c3a.nativeElement.childNodes.length > 0) ||
(this.c3b.nativeElement.childNodes.length > 0)
);
}
}
I hope this helps someone.

How can I select a specific occurrence on the entire page using CSS?

I have tried :nth-of-type and :nth-child, but those don't work for selecting a specific instance on the entire page. For example, if I wanted to select the 8th div.box element below ("plane") how can I do this?
<div>
<div class="box">blueberry</div>
<div class="box">cherry</div>
<div class="box">pineapple</div>
<div class="box">orange</div>
<div class="box">grape</div>
</div>
<div>
<div class="box">car</div>
<div class="box">boat</div>
<div class="box">plane</div>
<div class="box">bike</div>
<div class="box">motorcycle</div>
</div>
You can do this with javascript. I don't think you can do such a specific task with CSS, but I'm not sure.
let base = document.getElementsByTagName("div");
let divs = [];
for (let i = 0; i < base.length; i++) {
let elems = base[i].getElementsByTagName("div");
for (let j = 0; j < elems.length; j++) {
divs.push(elems[j])
}
}
function findNElement(n) {
return divs[n-1].textContent; // This is to return text. Remove .textContent to remove th element
}
console.log(findNElement(8));
<div>
<div class="box">blueberry</div>
<div class="box">cherry</div>
<div class="box">pineapple</div>
<div class="box">orange</div>
<div class="box">grape</div>
</div>
<div>
<div class="box">car</div>
<div class="box">boat</div>
<div class="box">plane</div>
<div class="box">bike</div>
<div class="box">motorcycle</div>
</div>
You can use this snippet to select a certain element of the textContent of the element.
For the completely general case, where you do not know the document's structure (i.e. div.box elements are 'scattered' as you say) I think you'll need Javascript.
The element you want is
document.querySelectorAll('div.box')[7]
of course you'll want to check that it actually exists before trying to do anything with it so something like:
function findBox(n) { //n starts at 1
const boxes = document.querySelectorAll('div.box');
if (boxes.length >= n) return boxes[n-1];
else return false;
}
let n = 8;
let box = findBox(n);
if (box) {
box.style.backgroundColor = 'silver';
}
else {
console.log('There is no ' + n + 'div.box');
}
<div>
<div class="box">blueberry</div>
<div class="box">cherry</div>
<div class="box">pineapple</div>
<div class="box">orange</div>
<div class="box">grape</div>
</div>
<div>
<div class="box">car</div>
<div class="box">boat</div>
<div class="box">plane</div>
<div class="box">bike</div>
<div class="box">motorcycle</div>
</div>

AngularJs ng-style ::after

I'm trying to edit the border-bottom-color individual depending on a property of the element in ng-repeat.
Here is an example how the html is structured. The changed style is
.active-tool::after {border-bottom-color: rgb(247, 153, 248)}
html:
<div data-ng-repeat="row in rows">
<div class='container'>
<div
data-ng-style="getPrimaryColor(tvShow)"
class='folder tvshow'
data-ng-class="isActiveFolder(tvShow)"
id='{{tvShow.id}}'
data-ng-repeat="tvShow in row track by $index">
<div data-ng-click="setSelectedTvShow(tvShow)">
<p class="tvshow-name">{{tvShow.name}}</p>
</div>
</div>
</div>
controller.js
$scope.isActiveFolder = function(tvShow) {
if($scope.selectedTvShow !== null && tvShow.id !== null) {
return $scope.selectedTvShow===tvShow.id ? 'active-tool' : '';
}
};
$scope.getPrimaryColor = function(tvShow) {
if($scope.selectedTvShow !== null) {
var result = '{' + tvShow.id + '.active-tool::after {border-bottom-color: rgb(247, 153, 248)}}';
console.log(result);
return result;
};
Any ideas how this could be done?
I use this quick hack:
put this inside your template:
<style type="text/css">
.active-tool::after {
border-bottom-color: {{getShowBorderColor(tvShow)}};
}
</style>
and then in your controller:
$scope.getShowBorderColor = function(tvShow){
return tvShow.color; // change this for how you want to calculate the color
};
You cannot use ng-style like this, because html style attribute does not support css selectors.
Actually, you can do it with no javascript at all:
markup:
<div data-ng-repeat="row in rows">
<div class='container'>
<div class='folder tvshow'
data-ng-class="{active-tool : selectedTvShow === tvShow.id}"
id='{{tvShow.id}}'
data-ng-repeat="tvShow in row track by $index">
<div data-ng-click="selectedTvShow = tvShow.id">
<p class="tvshow-name">{{tvShow.name}}</p>
</div>
</div>
</div>
</div>
css:
.active-tool::after {
border-bottom-color: rgb(247, 153, 248);
}

CSS3: Change Page Background Image if Image is Hovered

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 = '';
};

Resources