I am building a site and one of the components requires different text to be displayed in the same spot when you hover over different blocks. I am using jQuery to accomplish this and changing the html, however I am noticing since the text is different sizes it pushes down the div to allocate more room for the text.
Is it possible to keep the text transparent or something so the colour and html is changed at the same time, to give the illusion it is popping in?
Please see code below:
$(".stats-text-1").hover(
function() {
$(".stats-text").html(
"Our client’s monetary milestones are driven by our social tactics and digital marketing."
);
},
function() {
$(".stats-text").html(" ");
}
);
$(".stats-text-2").hover(
function() {
$(".stats-text").html(
"Our experience is from more than just a couple of wins - it’s from learning through years of wins and losses."
);
},
function() {
$(".stats-text").html(" ");
}
);
$(".stats-text-3").hover(
function() {
$(".stats-text").html(
"Our clients currently see a minimum average of 5.4 times return on ad spend."
);
},
function() {
$(".stats-text").html(" ");
}
);
.stats-1 {
font-size: 12vw;
font-weight: bold;
color: rgba(255, 255, 255, 0.83);
}
.stats-2 {
font-size: 2vw;
font-weight: bold;
color: #f2f2f2;
}
.stats-3 {
font-size: 2vw;
color: rgba(255, 255, 255, 0.6);
}
.stats-text {
padding-top: 1rem;
font-size: 2vw;
text-align: left;
color: #fff6f4;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="row">
<div class="col-1"> </div>
<div class="col-11 card-2-title">BEEN THERE, DONE THAT.</div>
<div class="card-2-title-mobile">BEEN THERE, DONE THAT.</div>
</div>
</div>
<div class="row stats-border">
<div class="col-1"> </div>
<div class="col-3 stats stats-text-1">
<h1 class="stats-1" style="text-align: center">11</h1>
<h2 class="stats-2" style="text-align: center">Million</h2>
<h3 class="stats-3" style="text-align: center">
Revenue Generated
</h3>
</div>
<div class="col-spec24"> </div>
<div class="col-3 stats stats-text-2">
<h1 class="stats-1" style="text-align: center">9</h1>
<h2 class="stats-2" style="text-align: center">Years</h2>
<h3 class="stats-3" style="text-align: center">
In The Making
</h3>
</div>
<div class="col-spec24"> </div>
<div class="col-3 stats stats-text-3">
<h1 class="stats-1" style="text-align: center">6</h1>
<h2 class="stats-2" style="text-align: center">Times</h2>
<h3 class="stats-3" style="text-align: center">
Return On Ad Spend
</h3>
</div>
<div class="col-1"> </div>
</div>
<div class="card-2-desktop" style="padding-bottom: 15vw">
<div class="row">
<div class="col-1"> </div>
<div class="col-7 stats-text" id="statsText"></div>
<div class="col-4"> </div>
</div>
</div>
The way I've adressed the problem is based on using CSS Grid to create the initial size of the element in which you're showing your messages, and also adding the messages to the HTML, rather than replacing text.
While you could – of course – establish the sizes of the elements by positioning the content off-screen to inform the necessary sizing, and then animate to those dimensions before showing the message on-screen, that's more work than feels necessary.
My suggested approach is below, with explanatory comments in the code itself:
// using the '.stats' selector to obtain a jQuery Object containing
// all of the elements with that class-name in the document,
// we then use the attr() method to set the custom data-index attribute
// for later use:
$('.stats').attr('data-index', function(i) {
return i + 1;
// rather than the hover() method we use the on() method instead to handle
// both 'mouseenter' and 'mouseleave' events, and we pass the Event Object,
// as 'evt', into the anonymous function:
}).on('mouseenter mouseleave', function(evt) {
// here we use jQuery's data() method to retrieve the value of the
// data-index custom-attribute:
let index = $(this).data('index');
// here we retrieve the .message element which has the same
// data-index attribute and attribute-value, which is also within
// a .marketing element:
$(`.marketing .message[data-index="${index}"]`)
// we then use the toggleClass() method to add, or remove,
// the 'visible' class to the relevant .message element
// depending on whether the assessment returns true or false;
// if the evt.type is exactly-equal to 'mouseenter' the
// assessment returns Boolean true, and the class is added;
// otherwise Boolean false is returned and the class is
// removed:
.toggleClass('visible', evt.type === 'mouseenter');
});
$('input').on('input', function(){
$('main').css('--textSize',`${$(this).val()}rem`)
}).change();
/* a basic CSS reset to ensure that all elements
are sized in similar ways: */
*,
::before,
::after {
box-sizing: border-box;
font-size: 1rem;
font-weight: normal;
line-height: 1.5;
margin: 0;
padding: 0;
}
/* defining CSS Grid as the layout of
the <main> element: */
main {
display: grid;
/* defining three equal-width columns, each
of one fractional-unit of the available
space: */
grid-template-columns: repeat(3, 1fr);
margin: 0.5em auto;
width: 90vw;
}
/* I removed the inline <style> attribute from the
various elements, since it made the HTML noisier
than I'd like (adjust to taste of course): */
.stats > :is(h1, h2, h3) {
text-align: center;
}
/* I assumed that the messages should be full-width,
so here I defined the .marketing element should
start in the first track and end in the last: */
.marketing {
grid-column: 1 / -1;
}
/* again, using CSS Grid for the element that holds the
marketing messages: */
.marketing > div {
display: grid;
/* defining a single named area in which the marketing
claims should appear: */
grid-template-areas: "claims";
}
.marketing > div > .message {
/* here we position all of the .message elements into
the same grid area; which allows the largest grid-item
to define the size of that grid area: */
grid-area: claims;
/* effectively hiding the elements, and centring the text: */
opacity: 0;
pointer-events: none;
text-align: center;
user-select: none;
z-index: -1;
}
/* this is the 'background' element against which the .message
will be displayed, this can be easily adjusted or the
.message elements themselves can have their own background: */
.marketing > div > .mask {
background: linear-gradient(135deg, lime, #ffaf);
grid-area: claims;
}
/* when the 'visible' class-name is added to the .message elements
this CSS promotes their visibility, by raising their opacity to
1 (fully visible), raising their z-index above the background
and re-enabling pointer events and user-selection: */
.marketing > div > .message.visible {
opacity: 1;
pointer-events: auto;
user-select: auto;
z-index: 2;
}
.message:nth-child(2) {
font-size: var(--textSize, inherit);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!-- this is absolutely irrelevant to the demo, but does demonstrate how the
grid size automatically adjusts to the size of the largest element -->
<label>Adjust text-size of the second <code>message</code> element to:
<input type="number"
min="0.5"
max="20"
step="0.5"
value="1" /></label>
<!-- using the <main> element as a wrapping block for the posted content; -->
<main>
<!-- I removed the 'stats-text-n' class-name, since that would seem
to be more use as an id (given its role in uniquely identifying
a specific element, and also because that makes your code
inherently non-reusable; whereas each element has a 'stats' class-
name which allows us to generalise the JavaScript -->
<div class="col-3 stats">
<h1 class="stats-1">11</h1>
<h2 class="stats-2">Million</h2>
<h3 class="stats-3">Revenue Generated</h3>
</div>
<div class="col-3 stats">
<h1 class="stats-1">9</h1>
<h2 class="stats-2">Years</h2>
<h3 class="stats-3">In The Making</h3>
</div>
<div class="col-3 stats">
<h1 class="stats-1">6</h1>
<h2 class="stats-2">Times</h2>
<h3 class="stats-3">Return On Ad Spend</h3>
</div>
<!-- here I added the 'marketing' class-name, since the 'card-2-desktop' seems
as though it may be a product of a framework -->
<div class="card-2-desktop marketing">
<div class="row">
<!-- these messages were taken from your jQuery code, and placed inside of
the '.row' element, along with a custom data-* attribute which indicates
which of the '.stats' elements it refers to: -->
<div class="message" data-index="1">Our client’s monetary milestones are driven by our social tactics and digital marketing.</div>
<div class="message" data-index="2">Our experience is from more than just a couple of wins - it’s from learning through years of wins and losses.</div>
<div class="message" data-index="3">Our clients currently see a minimum average of 5.4 times return on ad spend.</div>
<!-- an element to act as the background of the other elements, this is
entirely optional and largely irrelevant -->
<div class="mask"></div>
</div>
</div>
</main>
JS Fiddle demo.
Of course, anything that can be accomplished in jQuery can also be achieved in native JavaScript; again, explanatory notes are in the comments of the code below:
// we use Array.from() to convert the NodeList returned by
// document.querySelectorAll() into an Array, in order to
// use Array methods later:
const messages = Array.from(
// here we retrieve all .message elements within a .marketing
// element:
document.querySelectorAll('.marketing .message')
),
// defining the toggle function, using Arrow syntax, and passing
// the Event Object ('evt') into the function:
toggle = (evt) => {
// we use 'currentTarget' property of the Event Object to find
// the element to which the event-handler was bound, as opposed
// to the 'target' property which simply returns the element
// upon which the event was initially fired; from that element
// we retrieve the data-index attribute-value:
let index = evt.currentTarget.dataset.index,
// here we filter the Array of .message elements to find the
// element(s) matching the the supplied filter, using an
// Arrow function to pass the current Array-element into
// the function body:
message = messages.filter(
// here we're looking to retain elements whose data-index
// attribute-value matches that of the .stats element
// upon which the event-handler was triggered:
(msg) => msg.dataset.index === index
);
// Array.prototype.filter() returns an Array, so here we use
// Array.prototype.forEach() to iterate through that Array:
message.forEach(
// here we toggle the 'visible' class-name on the retained
// .message elements, if the Event-type (evt.type) is exactly
// equal to 'mouseenter' the assessment returns Boolean true,
// and the class-name is added; otherwise Boolean false is
// returned and the class-name is removed (this generates no
// error if the class-name addition or removal would match
// the existing state):
(msg) => msg.classList.toggle('visible', evt.type === 'mouseenter')
);
};
// here we retrieve all elements matching the supplied CSS selector,
// and use NodeList.prototype.forEach() to iterate over that NoseList:
document.querySelectorAll('.stats').forEach(
// here we pass in a reference to the current Node of the NodeList
// (stat) and the index of that Node in the NodeList (i):
(stat, i) => {
// here we set the data-index attribute to be equal to the index
// plus 1 (to match the 1-based index in the HTML attributes I
// added):
stat.dataset.index = i + 1;
// and then bind the toggle() function - note the deliberate
// omission of the parentheses in the below code - as the
// event-handler for both the 'mouseenter' and 'mouseeout'
// events:
stat.addEventListener('mouseenter', toggle);
stat.addEventListener('mouseleave', toggle);
});
// again, largely irrelevant to the demo but demonstrates how the font-size
// determines the grid-area size to avoid size jumps between 'empty' and
// 'populated':
document.querySelector('input').addEventListener('input', (evt) =>
document.querySelectorAll('.marketing')
.forEach(
(el) => el.style.setProperty(
'--textSize',
`${evt.currentTarget.value}rem`)
)
);
*,
::before,
::after {
box-sizing: border-box;
font-size: 1rem;
font-weight: normal;
line-height: 1.5;
margin: 0;
padding: 0;
}
main {
display: grid;
grid-template-columns: repeat(3, 1fr);
margin: 0.5em auto;
width: 90vw;
}
.stats > :is(h1, h2, h3) {
text-align: center;
}
.marketing {
grid-column: 1 / -1;
}
.marketing > div {
display: grid;
grid-template-areas: "claims";
}
.marketing > div > .message {
grid-area: claims;
opacity: 0;
pointer-events: none;
text-align: center;
user-select: none;
z-index: -1;
}
.marketing > div > .mask {
background: linear-gradient(135deg, lime, #ffaf);
grid-area: claims;
}
.marketing > div > .message.visible {
opacity: 1;
pointer-events: auto;
user-select: auto;
z-index: 2;
}
.message:nth-child(2) {
font-size: var(--textSize, inherit);
}
<label>Adjust text-size of the second <code>message</code> element to:
<input type="number" min="0.5" max="20" step="0.5" value="1" /></label>
<main>
<div class="col-3 stats">
<h1 class="stats-1">11</h1>
<h2 class="stats-2">Million</h2>
<h3 class="stats-3">Revenue Generated</h3>
</div>
<div class="col-3 stats">
<h1 class="stats-1">9</h1>
<h2 class="stats-2">Years</h2>
<h3 class="stats-3">In The Making</h3>
</div>
<div class="col-3 stats">
<h1 class="stats-1">6</h1>
<h2 class="stats-2">Times</h2>
<h3 class="stats-3">Return On Ad Spend</h3>
</div>
<div class="card-2-desktop marketing">
<div class="row">
<div class="message" data-index="1">Our client’s monetary milestones are driven by our social tactics and digital marketing.</div>
<div class="message" data-index="2">Our experience is from more than just a couple of wins - it’s from learning through years of wins and losses.</div>
<div class="message" data-index="3">Our clients currently see a minimum average of 5.4 times return on ad spend.</div>
<div class="mask"></div>
</div>
</div>
</main>
JS Fiddle demo.
References:
CSS:
Attribute-selectors.
:is().
JavaScript:
Arrow functions.
Array.from().
Array.prototype.filter().
document.querySelectorAll().
Element.classList API.
EventTarget.addEventListener().
HTMLElement.dataset.
NodeList.prototype.forEach().
Template literals.
jQuery:
attr().
change().
data().
on().
toggleClass().
val().
Related
I'm starting out with divs arranged the following way. Two divs, main-top and main-bottom start out hidden and I want them to appear after they've been moved into the right spot. I want to move main-top to appear after header, and move main-bottom to appear before footer, and after they're moved, show the divs. How do you move these divs with jQuery? The final order should be header, main-top, main-bottom, footer
<div id='main'>
<div id='main-top'>Main Top</div>
<div id='main-bottom'>Main Bottom</div>
<div id='header'>Header</div>
<div id='footer'>Footer</div>
</div>
<style>
#main-top {display: none;}
#main-bottom {display: none;}
<style>
<script>
//*Move 'main-top' to appear after 'header'*
$('#main-top').show(); // Show div after it's moved
//*Move 'main-bottom' to appear before 'footer'*
$('#main-bottom').show(); // Show div after it's moved
</script>
You can use insertBefore and insertAfter funstions
$('#main-top').insertAfter('#header').show();
$('#main-bottom').insertBefore('#footer').show();
#main-top {display: none;}
#main-bottom {display: none;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id='main'>
<div id='main-top'>Main Top</div>
<div id='main-bottom'>Main Bottom</div>
<div id='header'>Header</div>
<div id='footer'>Footer</div>
</div>
This problem can be solved in a couple of ways, one way – with caveats – using CSS (with either CSS flex-box or Grid layouts) and another with JavaScript/jQuery.
First, with CSS flexbox:
/* we use flexbox layout on the element which
contains the elements you wish to rearrange,
which causes them to become flex-items, and
this allows us to use the 'order' property
to visually arrange them: */
#main {
display: flex;
flex-direction: column;
}
/* here we select the elements and use the
'order' property with a numerical value
to place them in the correct order within
their parent element: */
#header {
order: 1;
}
#main-top {
order: 2;
}
#main-bottom {
order: 3;
}
#footer {
order: 4;
}
<div id='main'>
<div id='main-top'>Main Top</div>
<div id='main-bottom'>Main Bottom</div>
<div id='header'>Header</div>
<div id='footer'>Footer</div>
</div>
and, with CSS Grid:
/* we use grid layout on the element which
contains the elements you wish to rearrange,
which causes them to become grid-items, and
this allows us to use the 'order' property
to visually arrange them: */
#main {
display: grid;
grid-auto-flow: row;
}
/* here we select the elements and use the
'order' property with a numerical value
to place them in the correct order within
their parent element: */
#header {
order: 1;
}
#main-top {
order: 2;
}
#main-bottom {
order: 3;
}
#footer {
order: 4;
}
<div id='main'>
<div id='main-top'>Main Top</div>
<div id='main-bottom'>Main Bottom</div>
<div id='header'>Header</div>
<div id='footer'>Footer</div>
</div>
There's a third CSS approach, again using CSS Grid layout, but taking advantage of the grid-template-areas and grid-area properties:
/* we use flexbox layout on the element which
contains the elements you wish to rearrange,
which causes them to become flex-items, and
this allows us to use the 'order' property
to visually arrange them: */
#main {
display: grid;
/* here we name several grid areas; each quoted string
names the areas in that row, here we have four named
rows and each row has only one column: */
grid-template-areas:
"header"
"main-top"
"main-bottom"
"footer";
}
/* here we select the elements and use the
'grid-area' property to place them each
in the correct grid-area: */
#header {
grid-area: header;
}
#main-top {
grid-area: main-top;
}
#main-bottom {
grid-area: main-bottom;
}
#footer {
grid-area: footer;
}
<div id='main'>
<div id='main-top'>Main Top</div>
<div id='main-bottom'>Main Bottom</div>
<div id='header'>Header</div>
<div id='footer'>Footer</div>
</div>
The caveat of these approaches is that they affect only the visual presentation; the DOM is untouched and the positioned elements – whether using order or grid-area – remain in their original order for those users using alternative means of consumption (such as screen-readers).
Using native JavaScript, which does modify the DOM order:
// we retrieve, and cache, the <div id="header"> element,
// and then its parentNode:
const header = document.getElementById('header'),
parent = header.parentNode;
// here we use the parentNode.insertBefore() method to
// place the 'header' before the 'parent' Node's
// first-child:
parent.insertBefore(header, parent.firstChild);
#main-top,
#main-bottom {
display: none;
}
#header+#main-top,
#header+#main-top+#main-bottom {
display: block;
}
<div id='main'>
<div id='main-top'>Main Top</div>
<div id='main-bottom'>Main Bottom</div>
<div id='header'>Header</div>
<div id='footer'>Footer</div>
</div>
This is functionally equivalent to your request, however your question specifically asks about:
moving the #main-top element after the #header element, and
moving the #main-bottom element before the #footer element.
With this in mind, if you really wish to move two elements instead of just the one, then the following should address your need in relation to the elements you wish to position alongside:
// retrieving and caching all elements using destructuring assigment:
const [parent, mainTop, mainBottom, header, footer] = document.querySelectorAll('div');
parent.insertBefore(mainTop, header.nextSibling);
parent.insertBefore(mainBottom, footer);
#main-top,
#main-bottom {
display: none;
}
#header+#main-top,
#header+#main-top+#main-bottom {
display: block;
}
<div id='main'>
<div id='main-top'>Main Top</div>
<div id='main-bottom'>Main Bottom</div>
<div id='header'>Header</div>
<div id='footer'>Footer</div>
</div>
Finally, of course, using jQuery if you wish to do so:
$('#header').after($('#main-top'));
$('#footer').before($('#footer'));
#main-top,
#main-bottom {
display: none;
}
#header+#main-top,
#header+#main-top+#main-bottom {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id='main'>
<div id='main-top'>Main Top</div>
<div id='main-bottom'>Main Bottom</div>
<div id='header'>Header</div>
<div id='footer'>Footer</div>
</div>
I have to make a real simple task but I cannot figure it out.
I have some cards that the user can select with a radio button. I want to higlight the selected card when the user click on the relative input radio.
I don't understand how can I select the closest class of the selected radio.
My HTML looks like this:
<div class="box">
<div class="box-content">
<input type="radio" name="box-input">
<label>Label One</label>
</div>
</div>
<div class="box">
<div class="box-content">
<input type="radio" name="box-input">
<label>Some Two</label>
</div>
</div>
And so on...
<button onclick="submit()">Submit</button>
If I do like this:
let boxes = document.querySelectAll('input');
const submit = () => {
for (let i=0;i<boxes.length;i++) {
boxes[i].checked? this.closest('.box').classList.add('selected'): console.log('nothing is selected')
}
}
It says that this.closest is undefined, and it works only if the user click on the submit button.
What I want to do is just add a class to div .box when the radio input is selected, and remove it when change the state to unselected.
I'd like also to avoid the inline HTML "onclick" if possible.
Please pure javascript only
EDIT
With the suggestion of #somethinghere I added onchange="change(this)" to each input radio and I change my script in this way:
const change = el => {
el.checked?el.closest('.box').classList.add('selected'):el.closest('.box').classList.remove('selected')
;
It works, it adds the class selected when I click on a input radio. But if I click on another input, then the class selected is not removed.
Suggestions?
Added code to change the style of closest class when input radio is selected
var radioAll = document.querySelectorAll('input');
for(var i = 0; i < radioAll.length; i++)
{
radioAll[i].onclick = function()
{
//remove selected class from all box classs
var boxElems = document.querySelectorAll(".box");
[].forEach.call(boxElems, function(el) {
el.classList.remove("selected");
});
if(this.checked)
{
this.closest('.box').classList.add('selected');
}
};
}
.selected{
background-color: coral;
}
<div class="box">
<div class="box-content">
<input type="radio" name="box-input">
<label>Label One</label>
</div>
</div>
<div class="box">
<div class="box-content">
<input type="radio" name="box-input">
<label>Some Two</label>
</div>
</div>
And so on...
<button onclick="submit()">Submit</button>
While you've already accepted an answer, I thought I'd add an alternative approach:
// get a reference to the <button> element; here you only have the one <button>,
// so document.querySelector() will suffice (as it returns either the first
// Node that matches the supplied selector or null):
let submitButton = document.querySelector('button'),
inputs = document.querySelectorAll('input');
// here we use the same Arrow function syntax, which does not - by design - get
// its own 'this' reference:
const submit = () => {
// since we have the <input> elements already we use that, along with the
// NodeList.prototype.forEach() method:
inputs.forEach(
// here 'input' is a reference to the current <input> element of the
// NodeList of <input> elements over which we're iterating.
// we use Element.closest() to find the relevant '.box' element, and
// use the Element.classList API to toggle the 'hasSelected'
// class-name based on the supplied 'switch', the 'input.checked'; if
// 'input.checked' is true the class-name is added to the '.box', if
// 'input.checked' is false the class-name is removed (if the class-name
// is already present, or not-present, when it's added or removed no
// error is thrown and it presents no problem):
(input) => input.closest('.box').classList.toggle('hasSelected', input.checked)
)
}
// using the EventTarget.addEventListener() method, in place of the obtrusive
// 'onclick' in-line event-handling; here we bind the submit() function
// (note the deliberate lack of parentheses) as the event-handler for the
// 'click' event:
submitButton.addEventListener('click', submit);
*,
::before,
::after {
box-sizing: border-box;
margin: 0;
padding: 0;
font-size: 1rem;
line-height: 1.5;
}
body>div {
display: flex;
justify-content: space-between;
width: 50vw;
margin: 1em auto;
}
div.box {
display: grid;
grid-template-columns: 1fr 30px;
grid-gap: 0 10px;
border: 2px solid transparent;
padding: 0.5em;
border-radius: 1em;
}
div.box.hasSelected {
border-color: limegreen;
}
div.box.hasSelected::after {
display: contents;
content: '✓';
font-weight: bold;
color: limegreen;
}
<div>
<div class="box">
<div class="box-content">
<label><input type="radio" name="box-input">
Label One</label>
</div>
</div>
<div class="box">
<div class="box-content">
<label><input type="radio" name="box-input">
Some Two</label>
</div>
</div>
<button>Submit</button>
</div>
JS Fiddle demo.
References:
Arrow functions.
Document.querySelector().
Element.classList API.
Element.closest().
EventTarget.addEventListener().
NodeList.prototype.forEach().
I am using a jQuery script (below) to get a height of one div and apply it to the one next to it. It works absolutely fine, but it gets wrong if I have another row with the same .app-screenshot class inside, which has a different height.
How can I make .app-screenshot-description class get the height only of the closest .app-screenshot class height?
function getImgHeight() {
var divHeight = $('.app-screenshot').height();
$('.app-screenshot-description').css('height', divHeight+'px');
}
<div class="row">
<div class="app-screenshot">This divs height is 690px</div>
<div class="app-screenshot-description">Second div</div>
</div>
<div class="row">
<div class="app-screenshot">This divs height is 540px</div>
<div class="app-screenshot-description">Second div</div>
</div>
Assuming your html structure is following this pattern
<div class="row">
<div class="app-screenshot"></div>
<div class="app-screenshot-description"></div>
</div>
You can try something like this.
$('.row').each(function(){
var divHeight = $(this).find('.app-screenshot').height();
$(this).find('.app-screenshot-description').css('height', divHeight+'px');
});
You can use closest as well as mentioned in the comment, but if heights are different for each div, you still need to loop over them using each
Search for the divs that are with common Y coordinate:
function getImgHeight() {
$('.app-screenshot').each(function(){
var divHeight = $(this).height();
var divPosition = $(this).offset().top;
$('.app-screenshot-description').each(function(){
if($(this).offset().top == divPosition)
{
/*console.log($(this).offset().top);*/
$(this).css('height', divHeight+'px');
}
});
});
}
You didn't say when your function is supposed to run, but if you loop over the rows, it can work:
$(".row").each(function(index, row){
$(row).find(".app-screenshot-description").css("height", $(row).find(".app-screenshot").css("height"));
});
.row { border:1px solid black;}
.row > div { border:1px dashed red; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="row">
<div class="app-screenshot" style="height:100px">This divs height is 100px</div>
<div class="app-screenshot-description">Second div</div>
</div>
<div class="row">
<div class="app-screenshot" style="height:25px">This divs height is 25px</div>
<div class="app-screenshot-description">Second div</div>
</div>
While you have, as I write this answer, already accepted another answer to your question, I thought I'd take a moment to offer a further answer, which hopefully provides some further alternatives that may may be of use to you. Particularly since the solutions I offer here require no JavaScript to use (although I do use jQuery to demonstrate the functionality of those solutions).
There are three obvious approaches to take that would allow your desired functionality in pure CSS; in this answer I'll go through those options in order of (personal) preference.
1: flexbox.
This approach takes advantage of the fact that flexbox sizes its child items to be the same cross-axis dimension (if the contents are arranged in a row then the cross-axis dimension would be the height, and if arranged in a column the cross-axis dimension would be width).
// This is purely to demonstrate that both elements take
// the same height (that of the 'tallest' sibling.
// binding the anonymous function of the on() method as
// the event-handler for the 'click' event:
$('#resizeHeight').on('click', function() {
// selecting all elements matching the selector,
// and using the css() method's anonymous function
// to style each '.app-screenshot' element
// independently:
$('.app-screenshot').css('height', function() {
// generating a random height up to a maximum of 500px:
let newHeight = Math.floor(Math.random() * 500);
// setting the height of the current element:
this.style.height = newHeight + 'px';
// setting the text of the current element:
this.textContent = newHeight;
});
// triggering the click event on page-load in order
// the elements have a randomly-assigned size on
// page-load:
}).click();
body {
padding-top: 3em;
}
#control {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 2.5em;
text-align: center;
}
.row {
/* setting the display to use the
flexbox layout: */
display: flex;
margin-bottom: 1em;
}
.row>div {
/* setting the flex-grow and flex-shrink
to be 1, and the flex-basis to be auto: */
flex: 1 1 auto;
}
/* Everything below is either for aesthetics
or simple visibility: */
.app-screenshot {
background-color: fuchsia;
}
.app-screenshot-description {
background-color: silver;
}
.app-screenshot::before {
content: "This element's height is: ";
}
.app-screenshot::after {
content: 'px.';
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="control">
<button id="resizeHeight">Randomise height of '.app-screenshot' elements</button>
</div>
<div class="row">
<div class="app-screenshot"></div>
<div class="app-screenshot-description">Second div</div>
</div>
<div class="row">
<div class="app-screenshot"></div>
<div class="app-screenshot-description">Second div</div>
</div>
2. CSS grids.
// This is purely to demonstrate that both elements take
// the same height (that of the 'tallest' sibling.
// binding the anonymous function of the on() method as
// the event-handler for the 'click' event:
$('#resizeHeight').on('click', function() {
// selecting all elements matching the selector,
// and using the css() method's anonymous function
// to style each '.app-screenshot' element
// independently:
$('.app-screenshot').css('height', function() {
// generating a random height up to a maximum of 500px:
let newHeight = Math.floor(Math.random() * 500);
// setting the height of the current element:
this.style.height = newHeight + 'px';
// setting the text of the current element:
this.textContent = newHeight;
});
// triggering the click event on page-load in order
// the elements have a randomly-assigned size on
// page-load:
}).click();
body {
/* Setting the display to use CSS grid layout: */
display: grid;
/* specifying the number of rows, to:
row 1: 3em high,
row 2: min-content,
row 3: min-content.
'min-content' directs the row to be the
smallest practicable size that will still
fully contain the content of the elements
in that row, and because the elements take
up the whole of the allocated space those
elements are of equal height: */
grid-template-rows: 3em min-content min-content;
}
.row {
/* setting the display of the .row elements
to also use grid layout: */
display: grid;
/* setting both columns to be 1 fractional unit
therefore both columns will be the same size;
this can, of course, be adjusted to taste
using any valid CSS grid length unit: */
grid-template-columns: 1fr 1fr;
margin-bottom: 1em;
}
.app-screenshot {
/* Assigning the .app-screenshot element(s) to
be positioned in the first column of its
parent: */
grid-column: 1;
background-color: fuchsia;
}
.app-screenshot-description {
/* Assigning the .app-screenshot-description
element(s) to be positioned in the second
column of its parent: */
grid-column: 2;
background-color: silver;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="control">
<button id="resizeHeight">Randomise height of '.app-screenshot' elements</button>
</div>
<div class="row">
<div class="app-screenshot"></div>
<div class="app-screenshot-description">Second div</div>
</div>
<div class="row">
<div class="app-screenshot"></div>
<div class="app-screenshot-description">Second div</div>
</div>
3. CSS tables.
Ideally this would not be used since – and this is a purely personal opinion – it feels too close to using a <table> element for layout reasons, and while we're not using a <table> element we are using this for, purely, layout reasons. Which still feels dirty to me. It is, though, an option that could be used.
While I would tend to avoid this solution it does have the potential advantage of simplicity, despite the associated 'smell,' and takes advantage of a table-cell's parent table-row being defined by the 'tallest' of its descendants.
// This is purely to demonstrate that both elements take
// the same height (that of the 'tallest' sibling.
// binding the anonymous function of the on() method as
// the event-handler for the 'click' event:
$('#resizeHeight').on('click', function() {
// selecting all elements matching the selector,
// and using the css() method's anonymous function
// to style each '.app-screenshot' element
// independently:
$('.app-screenshot').css('height', function() {
// generating a random height up to a maximum of 500px:
let newHeight = Math.floor(Math.random() * 500);
// setting the height of the current element:
this.style.height = newHeight + 'px';
// setting the text of the current element:
this.textContent = newHeight;
});
// triggering the click event on page-load in order
// the elements have a randomly-assigned size on
// page-load:
}).click();
.row {
/* forcing the '.row' element(s) to display
as a table-row: */
display: table-row;
}
.app-screenshot,
.app-screenshot-description {
/* forcing the selected elements to display
as table-cells: */
display: table-cell;
width: 50vw;
/* emulating the margin-bottom of previous
demos in this answer: */
border-bottom: 1em solid #fff;
}
.app-screenshot {
background-color: fuchsia;
}
.app-screenshot-description {
background-color: silver;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="control">
<button id="resizeHeight">Randomise height of '.app-screenshot' elements</button>
</div>
<div class="row">
<div class="app-screenshot"></div>
<div class="app-screenshot-description">Second div</div>
</div>
<div class="row">
<div class="app-screenshot"></div>
<div class="app-screenshot-description">Second div</div>
</div>
Please note that while I've used jQuery to allow the .app-screenshot elements to be resized neither that jQuery, nor any JavaSCript at all, is necessary to implement these posted solutions, it was used purely to demonstrate the functionality of the solutions.
I'm trying to use the slot API in this example:
<tabs-s>
<tab-c>
<tab-c>
</tabs>
where tabs-s is the component that wraps other components.Inside it I'm using the tag to render its dom but if I want the assigned nodes I also get the whitespaces (text nodes).
Is there a manner to avoid getting the text nodes when calling assignedNodes() method? This was not happening in Polymer 1.x
Thanks
Let say you want to create a featured section to present new items
the section needs to have some basic information and change colors.
The element will take the title, count and class from his parent
<featured-section class="blue">
<span slot="count">3</span>
<h1 slot="title">The title of the element go here</h1>
</featured-section>
Inside the element featured-section
<dom-module id="featured-section">
<template>
<section>
<div class="vertical-section-container">
<div class="circle-container">
<div class="circle">
<slot name="count"></slot>
</div>
</div>
<slot name="title"></slot>
<feature-box></feature-box>
<feature-grid></feature-grid>
</div>
</section>
</template>
But who is in charge of the class detail? The element itself featured-section
<custom-style>
<style include="shared-styles">
:host {
display: block;
background-color: var(--my-section-color);
}
:host(.blue) {
--my-section-color: #03A9F4;
}
:host(.green) {
--my-section-color: #8BC34A;
}
:host(.pink) {
--my-section-color: #FF6EB6;
}
::slotted(h1) {
color: #fff;
padding-bottom: 1.5em;
line-height: 48px;
}
</style>
</custom-style>
I have a problem where I will be displaying a variable number of items displayed, and they will have a margin setting which is updated uniformly over the set.
So basically to put it simple if I have a set that is [1,2,3,4,5] it might be something like:
1 2 3 4 5
while if the number of number were to double they would require the same amount of space:
1 2 3 4 5 6 7 8 9 10
I have some solutions for this but what hit me was that if I have an css-class (as the margin is uniform over the given set) I could share the layout. So I would have liked it if it were possible to update the class dynamically if that makes sense I haven't found any information of that being possible to do, so if we assume that in the first example the margin is something like margin-right: 10px; then if I could change the class, (similar to how I set style on an element I guess is how I am thinking, but for the whole class), it would be really smooth. So let's assume a functionality to do this I could have a class:
.myclass {
margin-right: 10px;
}
and through our magic function .myclass.setProperty('margin-right', '5px'); (or whatever the syntax would be :P). It would act as if i had defined the class:
.myclass {
margin-right: 5px;
}
I hope that is enough to grasp my ideas and problem thus far. The way I am going about it at the moment is that I use a class for all shared behavior and set style for each element. However this is a bit tedious as it become something like:
for (var i in mynumbers) {
mynumbers.style.marginRight = new_margin;
}
Where new_margin is calculated based on a scale (i.e. it could change many times in a short period of time).
So to the question-part. Is there perhaps a way to achieve something like the first part (a way to dynamically change a class), or any thoughts or ideas how to implement it if the way I am doing it feels like a bad idea or if you feel there are better ways of handling this.
Thanks for reading and hope you find the problem interesting.
Ah, just another typical use case of flex layout:
.container {
display: flex;
border: 1px solid;
justify-content: space-between;
}
<h2>5 elements in a line with equal width gaps</h2>
<div class="container">
<span>1</span><span>2</span><span>3</span><span>4</span><span>5</span>
</div>
<h2>10 elements in a line with equal width gaps</h2>
<div class="container">
<span>1</span><span>2</span><span>3</span><span>4</span><span>5</span>
<span>6</span><span>7</span><span>8</span><span>9</span><span>10</span>
</div>
If your browser supports the feature, you would see something like this:
Here for browser compatibility.
EDIT: another interesting use case, although OP didn't ask about:
.container {
display: flex;
border: 1px solid;
justify-content: space-between;
}
.container:before, .container:after {
content: '';
width: 0;
}
<h2>5 elements in a line with equal width gaps</h2>
<div class="container">
<span>1</span><span>2</span><span>3</span><span>4</span><span>5</span>
</div>
<h2>10 elements in a line with equal width gaps</h2>
<div class="container">
<span>1</span><span>2</span><span>3</span><span>4</span><span>5</span>
<span>6</span><span>7</span><span>8</span><span>9</span><span>10</span>
</div>
If your browser supports the feature, you would see something like this:
Space is divided equally not only between elements, but also include both ends of the container. And you could see how simple the code is!
This sounds like a job for table-layout: fixed. Your browser computes the width of each "column" (element) in the first "table-row" based on the number of columns in the "table".
.table {
display: table;
table-layout: fixed;
width: 100%;
}
.table > span {
display: table-cell;
text-align: center;
}
<div class="table">
<span>1</span>
<span>2</span>
<span>3</span>
<span>4</span>
<span>5</span>
<span>6</span>
<span>7</span>
<span>8</span>
<span>9</span>
</div>
<div class="table">
<span>1</span>
<span>2</span>
<span>3</span>
<span>4</span>
<span>5</span>
</div>
Yes. This is possible. One thing to be aware of is that changing a class's property may cause the browser's renderer to recalculate the entire page's layout (it needs to figure out how your class change affects the overall layout). So if you plan on changing your margin-right in a way to animate the change, it's not going to perform very well depending on the complexity of your page. That said, here is a quick and dirty implementation that should work on everything IE9+:
<html>
<head>
<style>
.thing {
display: inline-block;
width: 20px;
height: 20px;
background: #f00;
}
</style>
</head>
<body>
<div>
<div class="thing myClass"></div>
<div class="thing myClass"></div>
<div class="thing myClass"></div>
<div class="thing myClass"></div>
<div class="thing myClass"></div>
<div class="thing myClass"></div>
</div>
<div>
<button id="doit">Make bigger</button>
</div>
</body>
<script>
var styleTag = document.createElement("style"),
styleSheet, index, cssRule, style;
// Create a <style> tag that will hold our CSS rule
document.head.appendChild(styleTag);
styleSheet = styleTag.sheet;
// Insert an empty .myClass rule into the style sheet
index = styleSheet.insertRule(".myClass {}", styleSheet.cssRules.length);
// Get the 'style' object from the rule. This is nearly identical to elem.style
cssRule = styleSheet.cssRules[index];
style = cssRule.style;
// Sets .myClass { margin-right: 5px; }
style.marginRight = "5px";
// Demo to show that it works when you click a button
document.querySelector("#doit").addEventListener("click", function() {
style.marginRight = "20px";
});
</script>
</html>