Sort elements img alphabetically by alt in jQuery - javascript

I have few elements on side:
https://jsfiddle.net/vvbpvt0c/
<figure class="img-space">
<img src="http://placehold.it/150/30ac17" alt="qwqwqwaccusamus beatae ad facilis cum similique qui sunt">
<figcaption class="text-img">accusamus beatae ad facilis cum similique qui sunt</figcaption></figure>
and I want to sort them by img[alt] and show on side, sort() don't work.

So here's a possible way. The sort is done, and they're sorting by figcaption. If you want to do it by the alt text, then you might want to make sure they all have an alt and simply use $(a).find("img").attr("alt") instead of the selector I used. Credit where credit is due, I used http://james.padolsey.com/snippets/sorting-elements-with-jquery/ as a jumping-off point.
jQuery.fn.sortElements = (function() {
var sort = [].sort;
return function(comparator, getSortable) {
getSortable = getSortable || function() {
return this;
};
var placements = this.map(function() {
var sortElement = getSortable.call(this),
parentNode = sortElement.parentNode,
// Since the element itself will change position, we have
// to have some way of storing its original position in
// the DOM. The easiest way is to have a 'flag' node:
nextSibling = parentNode.insertBefore(
document.createTextNode(''),
sortElement.nextSibling
);
return function() {
if (parentNode === this) {
throw new Error(
"You can't sort elements if any one is a descendant of another."
);
}
// Insert before flag:
parentNode.insertBefore(this, nextSibling);
// Remove flag:
parentNode.removeChild(nextSibling);
};
});
return sort.call(this, comparator).each(function(i) {
placements[i].call(getSortable.call(this));
});
};
})();
$("button").on("click", function() {
$("figure").sortElements(function(a, b) {
var aStr = $(a).find("figcaption").text().toUpperCase(),
bStr = $(b).find("figcaption").text().toUpperCase();
return aStr.localeCompare(bStr);
})
})
img {
float: left;
}
figcaption {
float: left;
margin-left: 5px;
width: 100px;
}
figure {
clear: both;
height: 175px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="sort">
Sort them!
</button>
<figure class="img-space">
<img src="http://placehold.it/150/30ac17" alt="accusamus beatae ad facilis cum similique qui sunt">
<figcaption class="text-img">accusamus beatae ad facilis cum similique qui sunt</figcaption>
</figure>
<figure class="img-space">
<img src="http://placehold.it/150/30ac17" alt="accusamus beatae ad facilis cum similique qui sunt">
<figcaption class="text-img">aaassaccudsasamus beatae ad facilis cum similique qui sunt</figcaption>
</figure>
<figure class="img-space">
<img src="http://placehold.it/150/30ac17" alt="accusamus beatae ad facilis cum similique qui sunt">
<figcaption class="text-img">fdsffsfsaccusamus beatae ad facilis cum similique qui sunt</figcaption>
</figure>

Related

How can I dynamically store the height of a changing React component? [duplicate]

Is it possible to detect when an element reference changes it's height?
I tried using the following, but when the height of the element changes for whatever reason, the change is not detected. (Please consider that this must also work in IE11)
useEffect(() => {
// detect change in reference height
}, [elementRef])
I noticed another answer mentioning to use a ResizeObserver, but the answer is incomplete, so I'd like to add my own.
You can create a ResizeObserver inside a useEffect-hook, and then do what you want to do when the element changes it's size. Remember to disconnect from the resizeObserver in the cleanup function.
useEffect(() => {
if (!elementRef.current) return; // wait for the elementRef to be available
const resizeObserver = new ResizeObserver(() => {
// Do what you want to do when the size of the element changes
});
resizeObserver.observe(endpageRef.current);
return () => resizeObserver.disconnect(); // clean up
}, []);
The most efficient way is to use a resize observer.
You can set it up in a useEffect, and clear it when the component unmounts.
I think you can use elementRef.current.clientHeight in useEffect dependencies in order to listen to tag's height.
I test with this case and it worked.
function App() {
const tag = useRef();
const [height, setHeight] = useState(10);
useEffect(() => {
console.log("updated", tag?.current?.clientHeight);
}, [tag?.current?.clientHeight]);
useEffect(() => {
setInterval(() => {
setHeight((height) => height + 10);
console.log(height, tag.current.clientHeight);
}, 2000);
}, []);
return (
<div className="App" ref={tag}>
<div style={{ height: height, backgroundColor: "green" }}></div>
</div>
);
}
Here is the codesandbox https://codesandbox.io/embed/reactjs-playground-forked-b8j5n?fontsize=14&hidenavigation=1&theme=dark
You could just use window.
My example below is optimised with a cleanup removeEventListener function should the component unmount.
const BoxAndHeight = () => {
const ref = React.useRef(null);
const [height, setHeight] = React.useState(0);
const onResize = React.useCallback(() => {
if (ref.current) setHeight(ref.current.clientHeight);
}, []);
React.useEffect(() => {
window.addEventListener("resize", onResize);
onResize();
return () => {
window.removeEventListener("resize", onResize);
};
}, []);
return (
<div ref={ref} style={style}>
<h1>My height: {height}</h1>
<p>
{dummyContent}
</p>
</div>
);
}
// Render it
ReactDOM.createRoot(
document.getElementById("root")
).render(<BoxAndHeight />);
const style = {
border: "2px solid #ccc",
padding: "10px",
backgroundColor: "#eee"
};
const dummyContent = "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Distinctio, fugiat. Ex in neque velit perspiciatis recusandae, deleniti illum error ea distinctio obcaecati nisi deserunt ab unde corporis quas magnam quo cupiditate dolor dicta? Eos nostrum delectus suscipit! Hic corrupti, assumenda quo modi rem aperiam voluptas explicabo alias fuga error nulla. Eos tenetur voluptas repellat. Tempore, ab harum. Recusandae impedit adipisci soluta officia sunt quis voluptas, quae ea! Eveniet vero incidunt enim quod, voluptate fugiat maxime deserunt et laudantium quidem, ducimus sunt ratione voluptatem libero neque accusamus praesentium fugit doloremque est nisi excepturi. Quo inventore est soluta culpa quos? Minus, laudantium!";
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
Possible solution is to use a React Refs. Try this example as reference:
export default function Home() {
const divRef = createRef()
// ...
return (
<div style={{ height: '100vh', width: '100vw' }}>
<div
ref={divRef} // ...
></div>
</div>
)
}
Detailed code here.

Can you merge to contents of one div element with another

I have two <div> elements in my web app and I would like to get the contents of both and put them into a single new element. I have tried doing this in multiple ways but I am having trouble doing this without a huge lump of spaghetti code and variables. does anyone have a simple way of achieving this?
Note (from future me):
sorry, I wrote this so badly when I was younger, I tried to rephrase it so maybe others can gain something from the answers.
I realise that this is an old question at this point, but since it suddenly appeared on the homepage – and I saw the existing answer – I thought I'd offer another approach, using plain JavaScript:
// I don't enjoy typing all that much, so these are a couple of utilities to avoid
// typing 'document' and 'document.querySelector(...)':
const D = document,
get = (selector, context = D) => context.querySelector(selector),
// a simple named function, written using Arrow function syntax (as there's no
// need to use 'this');
// the function takes three arguments, the first of which is the
// startNode: a reference to a node in the document, and
// n: an Integer which is the number of siblings in the document you wish to
// 'merge' together; this has a default argument of 0 which means the function
// does nothing,
// elementsOnly: Boolean, default of true; if this is true we only worry about
// merging element-nodes (<div>,<p>...) into the startNode; if false then we
// also merge text-nodes, comment-nodes, etc:
merge = (startNode, n = 0, elementsOnly = true) => {
// if there is no startNode (or startNode is falsey), we quit the function:
if (!startNode) {
return false;
}
// otherwise we initialise a counter from the supplied variable of 'n',
// we use parseInt(), with a radix of 10 (for base-10 numbering), on
// the supplied number in case it's somehow a string, or a number in
// another base:
let counter = parseInt(n, 10),
nodeTypes = elementsOnly === true ? 'nextElementSibling' : 'nextSibling';
// while the counter variable is truthy:
while (counter) {
// we assign either the startNode node's next element-sibling (if
// elementsOnly is equal to true) or the next sibling node (if
// elementsOnly is equal to false):
let next = startNode[nodeTypes];
// if there is no next-sibling node we break out of the while loop:
if (!next) {
break;
}
// if the nodeType of the 'next' node is
// equal to 1 (so the node is an element-
// node):
if (next.nodeType === 1) {
// while the element-node has a first-child:
while (next.firstChild) {
// we append that first-child to the startNode:
startNode.append(next.firstChild);
}
// once emptied, we remove the 'next' node:
next.remove();
// otherwise:
} else {
// we append the 'next' node to the startNode:
startNode.append(next);
}
// finally decrementing the counter variable for
// the next iteration:
counter--;
}
};
// calling the function:
merge(
// retrieving the first element in the document that matches the
// supplied CSS selector:
get('main > div'),
// passing in 3 as the value of 'n':
3);
// in the rendered output note that the 'Text node' remains outside of the startNode
// the first <div> element, despite being its immediate sibling in the HTML; if you
// inspect the document with dev tools you'll see the following comment-node also
// remains outside of the first <div>.
*,
::before,
::after {
box-sizing: border-box;
font-family: Roboto, system-ui;
line-height: 1.4;
margin: 0;
padding: 0;
}
main {
counter-reset: para;
border: 1px solid #000;
margin-block: 1em;
margin-inline: auto;
padding: 1em;
width: clamp(15rem, 70vw, 1200px);
}
div {
padding: 0.5em;
}
div:not(:first-child) {
margin-block-start: 1em;
}
div:nth-child(odd) {
background-color: hsl(70 80% 70% / 0.5);
}
div:nth-child(even) {
background-color: hsl(140 80% 70% / 0.5);
}
p::first-line {
font-style: italic;
}
p::before {
counter-increment: para;
content: counter(para, decimal-leading-zero) ': ';
font-weight: 800;
}
<main>
<div>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sapiente minus placeat, ipsum culpa voluptates sed. Ex sapiente labore beatae, enim repellendus, maxime tempora consectetur est, laborum explicabo ab. Ratione, vitae.</p>
</div>Text node<!-- comment node --><div>
<p>Reprehenderit ratione laudantium fugit fuga, alias praesentium. Repellat minus eaque aliquid quisquam odio provident ab sapiente, doloremque vero fugiat voluptates itaque ratione ex, doloribus, rem cumque assumenda. Ullam, quod, ut.</p>
</div>
<div>
<p>Assumenda quaerat magni, voluptatem est sit quia nesciunt fuga doloribus, beatae temporibus deserunt reiciendis suscipit excepturi animi maiores itaque, iusto consectetur, perspiciatis. Minima eligendi debitis suscipit quibusdam, odit, perspiciatis commodi!</p>
</div>
<div>
<p>Expedita ducimus facilis, quis architecto molestiae tempora provident officia obcaecati, repellat consectetur tempore non debitis quae dolores quaerat est maiores modi ratione similique ad consequuntur impedit asperiores numquam. Debitis, illum!</p>
</div>
<div>
<p>Odit quis expedita, architecto sed eum cum sunt deserunt facilis recusandae quia animi illo est, temporibus, accusantium amet. Numquam accusantium culpa eum blanditiis magnam consectetur soluta praesentium nam earum laboriosam.</p>
</div>
</main>
JS Fiddle demo.
// I don't enjoy typing all that much, so these are a couple of utilities to avoid
// typing 'document' and 'document.querySelector(...)':
const D = document,
get = (selector, context = D) => context.querySelector(selector),
// a simple named function, written using Arrow function syntax (as there's no
// need to use 'this');
// the function takes three arguments, the first of which is the
// startNode: a reference to a node in the document, and
// n: an Integer which is the number of siblings in the document you wish to
// 'merge' together; this has a default argument of 0 which means the function
// does nothing,
// elementsOnly: Boolean, default of true; if this is true we only worry about
// merging element-nodes (<div>,<p>...) into the startNode; if false then we
// also merge text-nodes, comment-nodes, etc:
merge = (startNode, n = 0, elementsOnly = true) => {
// if there is no startNode (or startNode is falsey), we quit the function:
if (!startNode) {
return false;
}
// otherwise we initialise a counter from the supplied variable of 'n',
// we use parseInt(), with a radix of 10 (for base-10 numbering), on
// the supplied number in case it's somehow a string, or a number in
// another base:
let counter = parseInt(n, 10),
nodeTypes = elementsOnly === true ? 'nextElementSibling' : 'nextSibling';
// while the counter variable is truthy:
while (counter) {
// we assign either the startNode node's next element-sibling (if
// elementsOnly is equal to true) or the next sibling node (if
// elementsOnly is equal to false):
let next = startNode[nodeTypes];
// if there is no next-sibling node we break out of the while loop:
if (!next) {
break;
}
// if the nodeType of the 'next' node is
// equal to 1 (so the node is an element-
// node):
if (next.nodeType === 1) {
// while the element-node has a first-child:
while (next.firstChild) {
// we append that first-child to the startNode:
startNode.append(next.firstChild);
}
// once emptied, we remove the 'next' node:
next.remove();
// otherwise:
} else {
// we append the 'next' node to the startNode:
startNode.append(next);
}
// finally decrementing the counter variable for
// the next iteration:
counter--;
}
};
// calling the function:
merge(
// retrieving the first element in the document that matches the
// supplied CSS selector:
get('main > div'),
// passing in 3 as the value of 'n':
3,
// setting (Boolean) false as the argument for elementsOnly,
// so this time we include all nodes not just element-nodes:
false);
// this time the rendered output will move both the text-node and
// the HTML comment inside of the startNode (first <div> element).
*,
::before,
::after {
box-sizing: border-box;
font-family: Roboto, system-ui;
line-height: 1.4;
margin: 0;
padding: 0;
}
main {
counter-reset: para;
border: 1px solid #000;
margin-block: 1em;
margin-inline: auto;
padding: 1em;
width: clamp(15rem, 70vw, 1200px);
}
div {
padding: 0.5em;
}
div:not(:first-child) {
margin-block-start: 1em;
}
div:nth-child(odd) {
background-color: hsl(70 80% 70% / 0.5);
}
div:nth-child(even) {
background-color: hsl(140 80% 70% / 0.5);
}
p::first-line {
font-style: italic;
}
p::before {
counter-increment: para;
content: counter(para, decimal-leading-zero) ': ';
font-weight: 800;
}
<main>
<div>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sapiente minus placeat, ipsum culpa voluptates sed. Ex sapiente labore beatae, enim repellendus, maxime tempora consectetur est, laborum explicabo ab. Ratione, vitae.</p>
</div>
<div>
<p>Reprehenderit ratione laudantium fugit fuga, alias praesentium. Repellat minus eaque aliquid quisquam odio provident ab sapiente, doloremque vero fugiat voluptates itaque ratione ex, doloribus, rem cumque assumenda. Ullam, quod, ut.</p>
</div>
<!-- comment node -->
<div>
<p>Assumenda quaerat magni, voluptatem est sit quia nesciunt fuga doloribus, beatae temporibus deserunt reiciendis suscipit excepturi animi maiores itaque, iusto consectetur, perspiciatis. Minima eligendi debitis suscipit quibusdam, odit, perspiciatis
commodi!
</p>
</div>
<div>
<p>Expedita ducimus facilis, quis architecto molestiae tempora provident officia obcaecati, repellat consectetur tempore non debitis quae dolores quaerat est maiores modi ratione similique ad consequuntur impedit asperiores numquam. Debitis, illum!</p>
</div>
<div>
<p>Odit quis expedita, architecto sed eum cum sunt deserunt facilis recusandae quia animi illo est, temporibus, accusantium amet. Numquam accusantium culpa eum blanditiis magnam consectetur soluta praesentium nam earum laboriosam.</p>
</div>
</main>
JS Fiddle demo.
References:
Arrow functions.
document.querySelector().
document.querySelectorAll().
Element.append().
Element.nextElementSibling.
Element.querySelector().
Element.querySelectorAll().
Element.remove().
if...else.
Node.nextSibling.
Node.nodeType.
while () {...}.
There are several issues with your code:
JavaScript variables cannot start with a number (0-9).
There isn't a div with an id of 1.
$().html is a function and must be called as $().html() with parentheses.
By fixing those issues and adding another line of JavaScript, you'll get a complete webpage that merges the HTML content of both divs into the first one, then removes the second div:
<!DOCTYPE html>
<div id='one'>One</div>
<div id='two'>Two</div>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script>
var one = $('#one').html();
var two = $('#two').html();
$('#one').html(one + two);
$('#two').remove();
</script>

elements.style.backgroundColor returns undefined [duplicate]

This question already has answers here:
Get a CSS value with JavaScript
(8 answers)
What do querySelectorAll and getElementsBy* methods return?
(12 answers)
Closed 3 years ago.
I'm trying to use javascript to see if a class has a specific background color but every time I tried something it always returned undefined. I have tried a lot of things so I'm at a loss here.
CSS
.ChangeColors {
background-color: #ff801b;
color:black;
}
Javascript
function ChangeColors(){
var all = document.getElementsByClassName('ChangeColors');
if (all.style.backgroundColor == "#ff801b") {
for (var i = 0; i < all.length; i++) {
all[i].style.color = 'white';
all[i].style.backgroundColor = 'blue';
}
} else {
for (var i = 0; i < all.length; i++) {
all[i].style.color = 'black';
all[i].style.backgroundColor = '#ff801b';
console.log(help)
}
}
}
You can have seperate classes for changing CSS properties like
.ChangeColors {
width: 100%;
height: 100px;
margin: 10px;
color: black;
}
.orangecolor {
background-color: #ff801b;
}
.bluecolor {
background-color: blue;
color:white;
}
and can loop through nodes and add/remove class according to the requirement
function ChangeColors() {
var all = document.querySelectorAll(".ChangeColors");
for (var i = 0; i < all.length; i++) {
if (all[i].classList.contains("orangecolor")) {
all[i].classList.add("bluecolor");
} else {
all[i].classList.add("orangecolor");
}
}
}
Check fiddle for it
There are 3 problems.
getElementsByClassName returns a list of HTMLCollection
style property is used to get as well as set the inline style of an element.
<div style="color: red;"></div>
Your element does not have inline stlye attribute. So element.style.backgroundColor does not work.
element.style.backgroundColor does not return always HEX color even though you set HEX color. On some browser, it returns RGB color.
This is a bad appraoch. However, if you like it, use window.getComputedStyle(element)["background-color"] and convert it to hex color if it is RGB and then compare.
function ChangeColors() {
var elements = document.getElementsByClassName('change-color');
for (let element of elements) {
var background = window.getComputedStyle(element)["background-color"]
console.log(background)
}
}
ChangeColors()
.change-color {
background-color: ##ff801b;
}
<div class="change-color">
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Iste deserunt officia voluptatum earum laboriosam? Est, ipsam. Ex veniam, incidunt, alias quasi aliquam sit eaque recusandae, expedita fugiat possimus a corrupti.
</div>
You may use two classes for applying color and background.
function ChangeColors() {
var elements = document.getElementsByClassName('change-color');
for (let element of elements) {
if (element.classList.contains("bg-blue")) {
element.classList.add('bg-orange');
} else {
element.classList.add('bg-blue')
}
}
}
ChangeColors()
.bg-blue {
background: blue;
color: white;
}
.bg-orange {
background: #ff801b;
color: black;
}
<div class="change-color">
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Iste deserunt officia voluptatum earum laboriosam? Est, ipsam. Ex veniam, incidunt, alias quasi aliquam sit eaque recusandae, expedita fugiat possimus a corrupti.
</div>
<div class="change-color bg-blue">
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Iste deserunt officia voluptatum earum laboriosam? Est, ipsam. Ex veniam, incidunt, alias quasi aliquam sit eaque recusandae, expedita fugiat possimus a corrupti.
</div>
Read these for more info.
getElementsByClassName
Window.getComputedStyle()
How to compare a backgroundColor in Javascript?
style.backgroundColor doesn't work?
If you use jQuery.
How can I compare two color values in jQuery/JavaScript?
You can also use jQuery for such util functions like below
var color = $('.ChangeColors').css( "background-color" );
Now compare your value with color it should not return undefined
if you don't want to use the whole jQ library you can do it with
getComputedStyle().
let all = document.querySelectorAll(".ChangeColors");
function ChangeColors() {
for (let i = 0; i < all.length; i++) {
const { style } = all[i];
if (
window.getComputedStyle(all[i], false).backgroundColor ===
"rgb(255, 128, 27)"
) {
style.color = "white";
style.background = "blue";
} else {
style.color = "black";
style.background = "#ff801b";
}
}
}
Here is an example https://codesandbox.io/s/eager-feistel-16l4m

Move div on mouse move

I am trying to adopt a script I have found in this question, but changing the image to some content seems to be harder than I thought.
The script is quite simple and should make the #content div move inside the holder on mousemove:
// written by Roko C. Buljan
var $mmGal = $('#holder'),
$mmImg = $('#content'),
damp = 10, // higher number = smoother response
X = 0, Y = 0,
mX = 0, mY = 0,
wDiff, hDiff,
zeno;
function motion(){
zeno = setInterval(function(){ // Zeno's paradox "catching delay"
X += (mX-X) / damp;
Y += (mY-Y) / damp;
$mmGal.scrollLeft(X*wDiff).scrollTop(Y*hDiff);
}, 26);
}
// Get image size after it's loaded and run our fn
$mmImg.one('load', function() {
wDiff = ( this.width/$mmGal.width() )-1,
hDiff = (this.height/$mmGal.height())-1;
}).each(function() {
if(this.complete) $(this).load();
});
$mmGal.mousemove(function(e) {
mX = e.pageX-this.offsetLeft;
mY = e.pageY-this.offsetTop;
}).hover(function( e ){
return e.type=='mouseenter'? motion() : setTimeout(function(){
clearInterval(zeno);
},1200); // clear if not used
});
Why doesn't the div #content move (text and image)?
Example JSBIN
I have updated the demo and added content on moving background image.
Check the demo:
$(function(){
var $mmGal = $('#mmGal'),
$mmImg = $('#mmImg'),
damp = 10, // higher number = smoother response
X = 0, Y = 0,
mX = 0, mY = 0,
wDiff, hDiff,
zeno;
function motion(){
zeno = setInterval(function(){ // Zeno's paradox "catching delay"
X += (mX-X) / damp;
Y += (mY-Y) / damp;
$mmGal.scrollLeft(X*wDiff).scrollTop(Y*hDiff);
}, 26);
}
// Get image size after it's loaded and run our fn
$mmImg.one('load', function() {
wDiff = ( this.width/$mmGal.width() )-1,
hDiff = (this.height/$mmGal.height())-1;
}).each(function() {
if(this.complete) $(this).load();
});
$mmGal.mousemove(function(e) {
mX = e.pageX-this.offsetLeft;
mY = e.pageY-this.offsetTop;
}).hover(function( e ){
return e.type=='mouseenter'? motion() : setTimeout(function(){
clearInterval(zeno);
},1200); // clear if not used
});
});
*{margin:0;padding:0;}
.main-wrapper {
position: relative;
width:150px;
height:150px;
}
#mmGal{
position:relative;
margin: 20px auto;
width:412px;
height:220px;
overflow:hidden;
background:#eee;
z-index: 0;
}
.content {
position: absolute;
left: 0;
right: 0;
top:0;
color: #ffffff;
padding: 10px;
z-index: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="main-wrapper">
<div class="content">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. quae molestias ad dolores qui blanditiis, Quas eaque soluta quia ipsa? aliquam?</p>
</div>
<div id="mmGal">
<img id="mmImg" src="http://www.wired.com/images_blogs/rawfile/2013/11/offset_WaterHouseMarineImages_62652-2-660x440.jpg">
</div>
</div>
My original script uses a .load() for the underlay image to load. Since you don't have a background image to move - you don't need necessarily that code part.
Calculate w/hDiff inside the mousemove event.
Also, use jQuery's .outerWidth() and .innerWidth() for the calculation:
jQuery(function($) {
const $mm = $('#holder'),
$mmCont = $('#content'),
damp = 10; // 1 = immediate, higher number = smoother response
let X = 0, Y = 0,
mX = 0, mY = 0,
wDiff = 0, hDiff = 0,
zeno, tOut;
$mm.on({
mousemove(ev) {
wDiff = ($mmCont.innerWidth() / $mm.outerWidth()) -1,
hDiff = ($mmCont.innerHeight() / $mm.outerHeight()) -1;
mX = ev.pageX - this.offsetLeft;
mY = ev.pageY - this.offsetTop;
},
mouseenter() {
clearTimeout(tOut);
clearInterval(zeno);
zeno = setInterval(function() { // Zeno's paradox "catching delay"
X += (mX - X) / damp;
Y += (mY - Y) / damp;
// Use CSS transition:
// $mmCont.css({ transform: `translate(${-X * wDiff}px, ${-Y * hDiff}px)` });
// Use Scroll:
$mm[0].scrollTo(X * wDiff, Y * hDiff);
}, 26);
},
mouseleave() {
// Allow the image to move for some time even after mouseleave
tOut = setTimeout(function() {
clearInterval(zeno);
}, 1200);
}
});
});
#holder {
background: gray;
width: 100%;
position: relative;
overflow: hidden;
height: 180px;
}
#content {
width: 150%;
height: 600px;
background: lightgray;
padding: 40px;
}
<div id="holder">
<div id="content">
<div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus dolore impedit dignissimos porro repellendus numquam aut quibusdam, consequuntur modi facere? Totam ut libero corporis sit sequi explicabo ab magni quaerat unde animi aliquid facere
necessitatibus, quae molestias ad dolores qui blanditiis, quisquam minima beatae autem iure. Neque animi tempore iste accusamus ut cum ipsam possimus, perspiciatis quia illo obcaecati sed molestiae amet architecto, nostrum cumque quaerat minima
minus, consequatur rem error nihil. Ipsa eveniet, praesentium suscipit optio blanditiis at, vel illum harum omnis quam. Quas eaque soluta quia ipsa? Illum inventore veritatis facilis eveniet voluptatibus atque laborum necessitatibus adipisci aliquam?</div>
<img src="https://i.stack.imgur.com/BfcTY.jpg">
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

Using jQuery to make multiple elements on a page the same height

I have a function I'm using to make any elements on a page with the class equalheight all the same height (the height of the tallest element):
equalheight = function(container){
var currentTallest = 0,
currentRowStart = 0,
rowDivs = new Array(),
$el,
topPosition = 0;
$(container).each(function() {
$el = $(this);
$($el).height('auto')
topPostion = $el.position().top;
if (currentRowStart != topPostion) {
for (currentDiv = 0 ; currentDiv < rowDivs.length ; currentDiv++) {
rowDivs[currentDiv].height(currentTallest);
}
rowDivs.length = 0; // empty the array
currentRowStart = topPostion;
currentTallest = $el.height();
rowDivs.push($el);
} else {
rowDivs.push($el);
currentTallest = (currentTallest < $el.height()) ? ($el.height()) : (currentTallest);
}
for (currentDiv = 0 ; currentDiv < rowDivs.length ; currentDiv++) {
rowDivs[currentDiv].height(currentTallest);
}
});
}
$(window).load(function() {
equalheight('.equalheight');
});
$(window).resize(function(){
equalheight('.equalheight');
});
The problem I'm running in to is that, let's say I have two rows of boxes. The first row I want all to be the same height, so I give them the class equalheight. Okay. Good. Now, I also want the second row of boxes to be all the same height, but just of those boxes in the second row.
Is there any way I can modify this code to allow me to set equal height of multiple groups of elements? For example, maybe something like setting the class as equalheight1, equalheight2, etc.?
EDIT: One thing I thought about was adding a data attribute on to each element and just calculate the tallest element for each element with the same data attribute..? Like:
<div class="equalheight" data-equalgroup="1"> </div>
We iterate through all elements and add them to the currentRow columns, and updating the maximum height. When we reach an element that its top differs from previous one, we update all heights of currentRow columns and then we set currentRow to a new one, finally after exiting from the loop, we update the last row columns' height.
equalheight = function (container) {
var currentRow = { cols: [], h: 0 };
var topPostion = -1;
$(container).each(function () {
var $el = $(this);
$($el).height('auto')
if (topPostion != $el.position().top) {
for (var j = 0; j < currentRow.cols.length; j++) {
currentRow.cols[j].height(currentRow.h);
}
topPostion = $el.position().top;
currentRow = { cols: [], h: 0 };
}
currentRow.cols.push($el);
if ($el.height() > currentRow.h) {
currentRow.h = $el.height();
}
});
for (var j = 0; j < currentRow.cols.length; j++) {
currentRow.cols[j].height(currentRow.h);
}
}
$(window).load(function() {
equalheight('.equalheight');
});
$(window).resize(function(){
equalheight('.equalheight');
});
here is a fiddle for it.
As far as I can tell you should be able to give each row a different class and simply run the function on each row;
$(window).load(function() {
equalheight('.equalheight-row-1');
equalheight('.equalheight-row-2');
});
$(window).resize(function(){
equalheight('.equalheight-row-1');
equalheight('.equalheight-row-2');
});
Although, looking closer at the code it seems to actually take care of that itself (with the currentRow stuff).
Edit: You don't actually have to give each row a different class, just do this;
$('.equalheight-row').each(function () {
equalheight($(this));
});
And the function should run individually on each row.
Looks to me it should be as simple as just adding it to the load and resize functions:
$(window).load(function() {
equalheight('.equalheight1');
equalheight('.equalheight2');
});
$(window).resize(function(){
equalheight('.equalheight1');
equalheight('.equalheight2');
});
Just note the classname.
You could add a second class to the divs you want to group up. Something like:
<div class="equalheight first"></div>
<div class="equalheight first"></div>
<div class="equalheight second"></div>
<div class="equalheight second"></div>
And then in your javascript:
$(window).load(function() {
equalheight('.first.equalheight');
equalheight('.second.equalheight');
});
You could fix this using flexbox layout, if your target doesn't require IE8 or older.
HTML
<div class="rows">
<div class="cell">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Necessitatibus illo quibusdam, perspiciatis vel officiis alias autem consequuntur labore mollitia omnis natus, fuga, illum id deleniti assumenda amet harum deserunt cum.</div>
<div class="cell"><div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquid provident, quaerat ducimus perspiciatis omnis sint aspernatur quibusdam saepe, quam similique eligendi nisi nesciunt atque laudantium quidem, doloribus culpa deserunt laboriosam.</div>
<div>Aspernatur porro vitae beatae nam fuga nisi magni eaque commodi odio voluptate veritatis laboriosam animi, ullam optio quidem sequi ducimus voluptas quaerat adipisci dignissimos. Velit sequi mollitia nemo veritatis optio.</div></div>
<div class="cell">Voluptate suscipit, laudantium. A, veniam repellat iste eligendi magnam at nisi quod suscipit laudantium tenetur porro accusamus quam quae nulla voluptatibus molestiae quisquam quos doloremque dolorum ipsam rem. Non, sunt.</div>
<div class="cell">Tempore praesentium voluptas earum harum numquam consequatur commodi omnis, debitis fuga suscipit, quae repellat hic iure molestiae doloribus. Eveniet voluptate quam placeat fugit iusto consequatur nam eaque porro eligendi itaque.</div>
<div class="cell">Sed deleniti ullam iusto pariatur natus, voluptatibus iste, ipsum assumenda dolor provident vel eum quisquam odit placeat in eligendi! Doloremque corporis maxime non culpa iusto rerum incidunt excepturi illum veniam.</div>
<div class="cell">Ullam provident fuga doloremque beatae possimus, facilis veritatis quae officia ut, atque expedita qui nulla adipisci, voluptate quas ipsam dolore vel architecto sapiente tenetur tempora laborum blanditiis libero. Saepe, ad!</div>
<div class="cell">Molestiae quam impedit sequi suscipit, rerum hic quia saepe excepturi, in assumenda inventore maxime vero. Earum aperiam dicta aut voluptas, rerum, repudiandae modi deserunt minima nostrum recusandae sequi, sunt natus!</div>
</div>
CSS
.rows {
margin: 15px auto;
width: 960px;
padding: 15px;
border: 1px solid;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
}
.cell {
min-width: 270px;
max-width: 275px;
-webkit-box-flex: 1;
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
padding: 15px;
border: 1px solid #F0F0F0;
margin: 7.5px;
}
demo: http://codepen.io/pjetr/pen/wabJYe
One of the efficient ways to achieve what you want is to divide your task in two functions.
Set heights of boxes according to rows in one function
Give class names to rows (like equalheight1, equalheight2..) based on top offset in another function.
JS:
$(document).ready(function () {
equalheight = function () {
var rowIndex = 1;
while ($('.equalheight' + rowIndex ).length) {
var currDivs = [];
$('.equalheight' + rowIndex ).each(function () {
currDivs.push($(this).height());
});
var maxHeight = Math.max.apply(Math, currDivs);
$('.equalheight' + rowIndex).each(function () {
$(this).height(maxHeight);
});
rowIndex++;
}
};
assignRows = function () {
// Reassign classes based on top offset
var allBoxes = $('.equalheight');
allBoxes.removeClass();
var rowIndex = 1;
var currentRowTop = 0;
var rowMap = {};
for (var i = 0; i < allBoxes.length; i++) {
if (rowMap[$(allBoxes[i]).position().top] === undefined)
rowMap[$(allBoxes[i]).position().top] = [];
rowMap[$(allBoxes[i]).position().top].push(allBoxes[i]);
}
for (var row in rowMap) {
for (var i = 0; i < rowMap[row].length; i++) {
$(rowMap[row][i]).addClass('equalheight equalheight' + rowIndex);
}
rowIndex++;
}
};
assignRows();
equalheight();
$(window).resize(function () {
assignRows();
equalheight();
});
});
In HTML, all boxes would have same class equalheight. For example:
<div class="equalheight">Element1</div>
<div class="equalheight">Element2</div>
<div class="equalheight">Element3</div>
JSFiddle: https://jsfiddle.net/w4j7Lwap/2/
I may be going a bit too far out of your depth but if you're using jQuery, why not build a plugin like this?
I'm going to use the same name attribute method suggested by Chris Dobson
$.fn.equalHeight=function(){
var $this=$(this);
heights={};
function setup(e){
var n=$(e).attr('name');
if(!heights[n])heights[n]=[];
heights[n].push($(e).height());
//console.log(heights);
};
function getHighest(name){
return Math.max.apply(null,heights[name]);
};
function setHeights(){
$this.height(getHighest($this.attr('name')));
//$this.foreach(function(){
// $this.height(getHighest($(this).attr('name')));
// });
};
$this.each(function(){
return setup($this);//think I need a return here?
});
$('window').on('resize, load',function(){
setHeights();
});
setHeights();
};
$(document).ready(function(){
$('.equalheight').equalHeight();
});
I also used this question to help in your answer ;)
Might want to get someone to double check it. I'm going to test it as well.
Try adding an attribute such as name* a new class to the elements - radio buttons use a similar method with the name element to determine which buttons are grouped together.
You can use then select by the equalheight class and the new class eg. equal1 to select only the elements from one group and deal with each group separately.
<div class="equalheight equal1"></div>
<div class="equalheight equal1"></div>
<div class="equalheight equal2"></div>
<div class="equalheight equal2"></div>
then in your JavaScript:
var equalGroupOne = $(".equalheight.equal1")
setHeightEqual(equalGroupOne);
var equalGroupTwo = $(".equalheight.equal2")
setHeightEqual(equalGroupTwo);
function setHeightEqual(equalGroup) {
//Your code here...
}
*My initial answer suggested the name attribute and jQuery attributeEquals selector, but the name attribute is not appropriate for div elements. An alternate approach is to use HTML5's data-* attributes and there is an argument for this being correct, but a new class seemed more appropriate (see my comment below for more).

Categories