I am fairly new to React. I am also new to asking questions on this forum.
I have a pretty basic two column layout and I am trying to dynamically set the image size in the RH column to match the height of the text content of the LH column.
I have figured out I can't get the scrollHeight value of the ref'd element until the page is rendered but I don't want to render the page until I know the scrollHeight value to render the image...and around in circles I go.
I can get the ref'd value if I put a console.log inside a useEffect() but I don't know to adapt this to help me solve the specific problem.
Can anyone help with a solution or another method to achieve this same idea?
const tickList = useRef();
useEffect(() => {
console.log(tickList);
}, [])
<div className='ticklist-container' ref={tickList}>
<TickCrossList items={tickListItems} />
</div>
<div className='image-container p-1 flex flex-center'>
<img
src={imageUrl}
alt={imageAltText}
style={{ width: tickList.current.scrollHeight * 1.77 }}
/>
</div>
I believe this is, at core, an XY problem. You're asking how to technically achieve a nearly impossible task (1) when you're actually trying to answer another question: how to make some text and an image look good side by side (2) (which, by and large, is not an actual coding problem - it's a graphical design problem).
Most notably, you're not providing either the text or the image, so you're asking for a general solution, which would work in all cases. That solution doesn't exist. Picture how the result would look if the text was 1 letter long and how it would look if it was 2 pages long.
Last, but not least, the problem is not strictly related to React. In general, you should ask yourself:
is this achievable in DOM (HTML + CSS + JS)?
does it need JS?
once you answer both of the above with "yes", and you know how the output should look like, the React part is generally easy.
In more detail...
(1). When you're changing the image size, provided you maintain its ratio, you're going to affect the paragraph's width, causing it to re-wrap, changing its height, potentially creating a loop. In many cases, you'd need to re-run the script multiple times until the text no longer re-wraps when adjusting the image height.
In some cases, depending on the amount of text, a solution doesn't even exist.
In other cases, the script will jump between two positions, basically two different numbers of text rows, each resizing the image, causing the text to jump to the other number of rows, causing the whole thing to tremble indefinitely. I've actually seen this quite a few times, on production websites. The general fix is to record each resizing step and, should the script get any of the previous values, kill it. An ugly fix for an ugly bug.
And, in some other cases, you might find multiple possible "solutions". Which one should the script pick?
Picture all this in the context of resize events (a user flipping their tablet from landscape to portrait and back) and you got a recipe for disaster, UX-wise.
Not to mention browsers nowadays allow users to override font-size or font-family, giving control over readability. How would your script cope with this change?
(2) How this problem is typically solved.
There are a few distinct considerations:
the image needs to have a minimum size, at which it conveys whatever message it carries. It needs to remain "readable" at all times. Not too small, but not too large, either. It has to be in balance with the amount of text.
if there's too much text, you either make its box scrollable (depending on case) or you crop the sides of the image (if image is "croppable" - from a graphical POV).
the text needs to remain readable (with ease) at all times (lines shouldn't be too long or too short)
typically, you want to roughly determine a ratio between the minimal image size and a paragraph width good for readability and set a consistent ratio (throughout the whole app/website) between the two (design consistency pays off in how your website is perceived as a whole - it provides rhythm and an overall feeling of confidence and reliability, when done right). Go with 1:1, 2:1, 3:1, 1:2, 1:3, based on paragraph length and readability. Another good ratio is 1:1.618 (the golden ratio - it's pleasing to the eye, without an exact explanation - debugging humans is particularly difficult)
a typical solution is to wrap the paragraph into a slightly bigger, visible box, with a slightly different background or border color. This gives you significantly more flexibility in matching the image height. See the examples below.
below a particular container width, you want the two elements (the image and the text box) to wrap, so they both remain readable and attractive (e.g: responsiveness)
A few bad examples you probably want to avoid (but you're currently asking for), followed by a possible answer to the Y problem:
.container {
display: flex;
max-width: 600px;
margin: 0 auto;
margin-top: 1rem;
justify-content: space-between
}
.proper img {
width: 50%;
}
.proper div {
background-color: #F5f5f5;
display: flex;
align-items: center;
justify-content: center;
padding: 2em;
}
.proper.two div {
background-color: transparent;
border: solid #ddd;
border-width: 1px 0 1px 1px;
}
<div class="container">
<h3>What you probably don't want</h3>
</div>
<div class="container">
<div>Spiff up your work with our random beer Lorem Ipsum generator. Beer!</div>
<img src="https://random-ize.com/lorem-ipsum-generators/beer/suds3.jpg" style="max-width: 528px">
</div>
<div class="container">
<div>Spiff up your work with our random beer Lorem Ipsum generator. Beer!
</div>
<img src="https://random-ize.com/lorem-ipsum-generators/beer/suds3.jpg" style="max-width: 515px">
</div>
<div class="container">
<div>Spiff up your work with our random beer Lorem Ipsum generator. Beer!
</div>
<img src="https://random-ize.com/lorem-ipsum-generators/beer/suds3.jpg" style="height: 1em">
</div>
<div class="container">
<div>Spiff up your work with our random beer Lorem Ipsum generator. More Beer!
</div>
<img src="https://random-ize.com/lorem-ipsum-generators/beer/suds3.jpg" style="height: 2em">
</div>
<div class="container">
<h3>What you probably want</h3>
</div>
<div class="container proper">
<div>Spiff up your work with our random beer Lorem Ipsum generator. Beer!</div>
<img src="https://random-ize.com/lorem-ipsum-generators/beer/suds3.jpg">
</div>
<div class="container proper two">
<div>Spiff up your work with our random beer Lorem Ipsum generator. Beer!</div>
<img src="https://random-ize.com/lorem-ipsum-generators/beer/suds3.jpg">
</div>
As a bonus, what golden ratio can do for you:
* {
box-sizing: border-box;
}
body {
background-color: #f5f5f5;
margin: 0;
padding: 0 2.1rem;
}
.container {
padding: 2.1rem 0;
max-width: 500px;
margin: 0 auto;
min-height: 100vh;
display: flex;
align-items: center;
}
.golden-ratio {
border-radius: .35rem;
overflow: hidden;
display: flex;
justify-content: space-between;
background-color: white;
color: #aaa;
aspect-ratio: 1.618;
box-shadow: 0 1px 5px 0 rgb(0 0 0 / 10%), 0 2px 2px 0 rgb(0 0 0 / 07%), 0 3px 1px -2px rgb(0 0 0 / 06%)
}
.image-container {
flex-grow: 1;
background: #ddd url('https://random-ize.com/lorem-ipsum-generators/beer/suds3.jpg') 100% /cover;
}
.content {
flex: 0 0 61.8%;
font-size: 1.5rem;
font-style: italic;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
padding: 0 10%;
}
#media (max-width: 600px) {
.golden-ratio {
flex-direction: column-reverse;
aspect-ratio: unset;
}
.content {
flex-basis: 50%;
padding: 3rem;
font-size: 1.2rem;
}
.image-container {
aspect-ratio: 4.16;
flex-basis: 50%;
}
}
<div class="container">
<div class="golden-ratio">
<div class="content">Spiff up your work with our random beer Lorem Ipsum generator. Beer!</div>
<div class="image-container"></div>
</div>
</div>
Related
I'm assuming this happens due to certain components taking longer to render on occasion, causing the browser to scroll to its position when it eventually renders?
I've searched around and tried using a scrollTo(0, 0) function at the end of my app.js, but is this the best way to deal with this issue? It works on most browsers but doesn't solve the problem on Firefox.
Here is the main app function that calls all the react components:
function App() {
return (
<div>
<ParaDiv>
<Nav />
<Fade>
<Splash />
</Fade>
</ParaDiv>
<Section2 />
<Card1></Card1>
<Fade>
<Popular />
</Fade>
</div>
);
}
There are several reasons why that happens, and it could be a symptom of a much bigger problem which can cause serious performance issues down the road. You can use tools like React DevTools/Profiler and see what components are causing unnecessary rendering, and then try and use hooks like useCallback/useMemo to solve that. Using Styled components within the functional component can cause similar behavior, even some css styling can cause that as well. I think it's better to find the root cause rather than look for a quick fix.
I would suggest using skeleton loaders instead, it is better practice.
Simple example found on Google:
https://codepen.io/ishaqzain/pen/yRMQOd
html
<div class="container">
<div class="post">
<div class="avatar"></div>
<div class="line"></div>
<div class="line"></div>
</div>
<div class="post">
<div class="avatar"></div>
<div class="line"></div>
<div class="line"></div>
</div>
<div class="post">
<div class="avatar"></div>
<div class="line"></div>
<div class="line"></div>
</div>
</div>
css
// forked from https://codepen.io/viktorstrate/pen/yoBRLy
$base-color: #F4F4F4
$shine-color: rgba(229,229,229,0.8)
$animation-duration: 2.0s
$avatar-offset: 52 + 16
// this unfortunately uses set px widths for the background-gradient, I never got around to changing it to work with all widths :(
#mixin background-gradient
background-image: linear-gradient(90deg, $base-color 0px, $shine-color 40px, $base-color 80px)
background-size: 600px
body
margin: 0
.container
height: 100vh
display: flex
flex-direction: column
justify-content: center
align-items: center
.post
width: 220px
height: 80px
.avatar
float: left
width: 52px
height: 52px
background-color: #ccc
border-radius: 25%
margin: 8px
#include background-gradient
animation: shine-avatar $animation-duration infinite ease-out
.line
float: left
width: 140px
height: 16px
margin-top: 12px
border-radius: 7px
#include background-gradient
animation: shine-lines $animation-duration infinite ease-out
.avatar + .line
margin-top: 11px
width: 100px
.line ~ .line
background-color: #ddd
#keyframes shine-lines
0%
background-position: -100px
40%, 100%
background-position: 140px
#keyframes shine-avatar
0%
background-position: -100px + $avatar-offset
40%, 100%
background-position: 140px + $avatar-offset
Maybe a few other things to mention, not actually an answer:
The scrollToTop should happen while the content is relatively "fixed", so nothing slides around that can change document height while scrolling up
you have to scroll to the top while loading new content, because if it's there you already want the user to be right there
you don't want the user to look at the "old" content while scrolling up, so you could make it disappear of put a modal layer over it, that prevents the user from clicking around
to show something like the skeleton loader from #Dorwey is a good idea, but that has to happen right after the click and then scrolling up
I am devolping app using angular js and ionic framework. I want to show border right only for odd numbers.
Here is my code:
<div class="media-body" style="padding-bottom:25px;">
<h2 class="align_center">{{services.name}}</h2>
<a href="#job/{{services.id}}">
<h2 class="align_center_des">{{services.description}}</h2>
</div>
</div></div>
Here is the Css
.col-32-custom {
width: 32%;
float: left;
margin-left: 1%;
border-right: 1px solid #E4E4E4;
margin-bottom: 31px;
height: 144px;
}
Here is fiddle:
https://jsfiddle.net/asetkL0n/
CSS also allows you to target specific odd or even elements. An example to that could be:
.col-32-custom {
width: 32%;
float: left;
margin-left: 1%;
margin-bottom: 31px;
height: 144px;
}
.col-32-custom:nth-child(odd) {
border-right: 1px solid #E4E4E4;
}
wherein, inside that nth-child, you can pass, "odd","even","2n","2n+1", or any expression in n.
I think the best solution is to use ng-class, so you have to create a class that will only add the border right.
I presume you are in an ng-repeat loop so the code will look like
<div data-ng-class="{border-right: ($index%2)===0}" class="col-32-custom">
Here you have the condition for the even number ($index%2)===0 so the div will have border-right class on event number.
you can use ng-class-odd / ng-class-even within ng-repeat to add specific classes to this items.
example here : ng-class-odd
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I am a HTML / CSS newbie.
I need to do something like this:
My web page is receiving sequences of events dynamically and I would like to visualize them on in the page.
I would like one sequence of events to be in a box, with lots of slots, and each slot has the event id.
So if I have several sequences, then I will have several such boxes.
However, the length of a sequence is dynamic. And the web page's window might be adjusted by the users, so even for a sequence, if it is too long or the window is too narrow, I have to break the box into several lines.
the above is my drawing of the design.
The A, B, etc, are the sequence title, then the numbers are the ids.
ideally, the space of all events / sequences should be as compact as possible.
And if a box has to change line, then it should be half-borded to indicate the continuous.
How can I do that? using CSS 3?
And also the framework I am using is AngularJS to control the data / UI binding, even if I manage to handle this case, how to dynamically bind the data to adjust this requirement?
Thanks
Doing this in CSS is tricky, because you want a border between elements only if those elements are on the same line. CSS doesn't know anything about wrapping.
I've solved the problem by:
Adding a left border on all boxes
Adding a right border on the last box only.
Adding a -1px left margin on all boxes except the first.
Placing the boxes in a container with overflow: hidden.
Having the right border on the last box only solves the right-hand issue.
The -1px left margin solves the left-hand issue.
Snippet:
.sequences {
overflow: hidden;
}
.sequence > div {
border: 1px solid black;
border-right: none;
height: 50px;
float: left;
margin-bottom: 10px;
}
.sequence > div:last-of-type {
border-right: 1px solid black;
margin-right: 20px;
}
.sequence > div:not(:first-of-type) {
margin-left: -1px;
}
.yellow div {background: yellow; width: 100px;}
.green div {background: lightgreen; width: 80px;}
.blue div {background: lightblue; width: 120px;}
<div class="sequences">
<div class="sequence yellow">
<div></div><div></div><div></div><div></div><div></div>
<div></div><div></div><div></div><div></div><div></div>
</div>
<div class="sequence green">
<div></div><div></div><div></div><div></div><div></div><div></div><div></div>
<div></div><div></div><div></div><div></div><div></div><div></div><div></div>
</div>
<div class="sequence blue">
<div></div><div></div><div></div>
<div></div><div></div><div></div>
</div>
</div>
You can solve this using CSS by doing something like this.
I've given each sequence element a top, left and bottom border. T
This will give the illusion of a right border when the elements are floated next to eachother but when they're the last on that line it will brake of as per your request.
I also added a right border to the last div element and the last div in each section.
Fiddle
div{
width: 100px;
height: 50px;
line-height: 50px;
text-align: center;
float:left;
background: #eee;
border: 1px solid #000;
border-width: 1px 0 1px 1px;
margin-bottom: 10px;
}
div.last{margin-right: 5px;}
div.last,
div:last-child{border-right-width: 1px;}
<div class="seq-1">1</div>
<div class="seq-1">2</div>
<div class="seq-1">3</div>
<div class="seq-1">4</div>
<div class="seq-1 last">5</div>
<div class="seq-2">1</div>
<div class="seq-2">2</div>
<div class="seq-2 last">3</div>
Edit:
I just noticed you wan't the border to be 0px/blank on the last element and the first element each row. Now that is a bit trickier.
I'm not positive there's a good solution to solving that using css since your sequences seem to be dynamic.
Please correct me if I'm wrong, but I think you need to use javascript to manage this.
Edit 2: CSS and JQuery solution
I made a quick jquery solution that utilies my previously provided CSS code.
The jQuery script removes the left border if the elements left offset(within it's parent) is 0 and if the element is not the first element in each sequenc(first class added).
Fiddle
var containerOffset = $('.container').offset().left;
setBorderWidth();
$(window).resize(function(){
setBorderWidth();
});
function setBorderWidth(){
$('.block').each(function() {
var childOffset = $(this).offset().left;
if(childOffset - containerOffset == 0 && !$(this).hasClass('first'))
$(this).css("border-left-width", "0px");
else
$(this).css("border-left-width", "1px");
});
}
.container{width: 100%;}
.block{
width: 100px;
height: 50px;
line-height: 50px;
text-align: center;
float:left;
background: #eee;
border: 1px solid #000;
border-width: 1px 0 1px 1px;
margin-bottom: 10px;
}
.block.last{margin-right: 5px;}
.block.last,
.block:last-child{border-right-width: 1px;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<div class="container">
<div class="seq-1 block first">1</div>
<div class="seq-1 block">2</div>
<div class="seq-1 block">3</div>
<div class="seq-1 block">4</div>
<div class="seq-1 block last">5</div>
<div class="seq-2 block first">1</div>
<div class="seq-2 block">2</div>
<div class="seq-2 block last">3</div>
</div>
What I would recommend is to have 3 CSS classes
1) beginning of sequence
2) middle of sequence
3) end of sequence
then display different borders using:
.beginning-of-seq {
border-top-style: solid;
border-right-style: none;
border-bottom-style: solid;
border-left-style: solid;
}
for instance.
about the angular part just use ng-repeat="seq in sequences" for instance and then render the sequence with the classes you created so it will look good (of course you need the scope to have the sequences)
<span ng-repeat="seq in sequences">
<span class="beginning-of-seq"> {{seq.title}} </span>
<span class="middle-of-seq ng-repeat="elem in seq.otherElements">{{elem}}</span>
<span class="end-of-seq"> {{seq.lastElem}} </span>
</span>
</span>
this is a bit crude and i don't know how you implemented it but it should give you an idea where to start
This HTML/CSS should do the trick. As you mentioned about the user having different resolutions, I've used percentages for the widths (depending on your scenario, media queries may be needed).
.container {
width: 30%; /*Change this to fit your design*/
}
.seq {
display: inline;
border: 0.1em solid #000;
margin-right: 1em;
}
.seq .item {
display: inline-block;
width: 5%; /*Change this to fit your design*/
margin-bottom: 1em;
}
.seq .item:not(:last-child) {
border-right: 0.1em solid #000;
}
<div class="container">
<div class="seq">
<div class="item item-title">A</div>
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
<div class="seq">
<div class="item item-title">B</div>
<div class="item">4</div>
<div class="item">5</div>
</div>
<div class="seq">
<div class="item item-title">C</div>
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
</div>
<div class="seq">
<div class="item item-title">D</div>
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
</div>
</div>
About the AngularJS binding:
In your controller define your array of sequences and some functions to manipulate the sequences:
$scope.sequences = [];
$scope.addSequence = function(sequenceName){
var newSequence = { name : sequenceName, events: [] };
$scope.sequences.push(newSequence);
};
$scope.addEventToSequence = function(sequenceName, event){
var sequence = getSequence(sequenceName); // write this function to get the right sequence from the array
sequence.events.push(event);
}
Now in your html loop over the sequences and events using ng-repeat
<ul>
<li ng-repeat="sequence in sequences">
<ul>
<li>{{sequence.name}}</li>
<li ng-repeat="event in sequence.event">{{event.name}}</li>
</ul>
</li>
</ul>
I'm pulling my hair here. Trying to come up with a simple responsive layout where two fluid boxes are aligned next to each other. The main box must always be centered in the browser window, while the other should be aligned beside it in its top right corner. See example image below -
Tried different approaches involving negative percentages and three-column faux layouts but it just doesn't work.
Demo: http://dabblet.com/gist/7201560
Markup:
<div class='container'>
<div class='main-col'></div>
<div class='right-col'></div>
</div>
CSS:
.container {
text-align: center;
}
.main-col, .right-col {
display: inline-block;
vertical-align: top;
text-align: left;
margin-right: -4px; /* css-tricks.com/fighting-the-space-between-inline-block-elements/ */
}
.main-col {
width: 50%;
margin-left: 20%; /* equal to .right-col's width */
}
.right-col {
width: 20%;
}
What's happening here:
The centered main column and right column have display: inline-block, and they're centered in the viewport by giving their container text-align: center. They're still not centered the way you want though. Since they're sibling elements you can use margin to push the main column to the left with a value equal to right-column's width, essentially centering itself.
Hi you can check my try in this link http://jsfiddle.net/WHq8U/17/.
I had to use a little jquery to calculate the sidebar absolute position. Let me know your opinion about this.
I have a website running on a mapping platform called Ushahidi. The default template is quite boxy so I was fiddling with the CSS and rounding everything off using border-radius.
It has helped with other elements but the map is such a square it won't ease up!
It might be that it's not possible, wondered if anyone here had any experience of this. The html using inspect element and view source are different. Not sure what this means exactly but guessing that the html is pulled in by the map provider?
Here is the html on view source:
<div class="map " id="map"></div>
<div style="clear:both;"></div>
<div id="mapStatus">
<div id="mapScale"></div>
<div id="mapMousePosition"></div>
<div id="mapProjection"></div>
<div id="mapOutput"></div>
</div>
I've added a screen of inspect element HTML too. Looks like it's using "Open Layers". I've heard of that but don't fully understand whats going on.
Is it possible to round the edges of my map? Here is the site if that helps: http://tinyurl.com/c8djrvr
Apply the border-radius to two layers.
CSS
div.map {
border: #999 1px solid;
width: 800px;
height: 366px;
position: relative;
height: 650px;
border-radius: 25px; /* ADD THIS */
}
#OpenLayers_Map_11_OpenLayers_ViewPort {
border-radius: 25px; /* ADD THIS */
}
It works. Use however many pixels you want. I used 25px.
I would suggest more general and safer CSS selectors (since ID #OpenLayers_Map_11_OpenLayers_ViewPort is generated by OpenLayers and it's value is unpredictable; and OL 2.12 and older produce ID's, that contain dot and are therefore unsuitable for CSS selectors):
.olMap, .olMapViewport {
border-radius: 25px;
}