What I have achieved:
// Get the id of the <path> element and the length of <path>
var myline = document.getElementById("myline");
var length = myline.getTotalLength();
circle = document.getElementById("circle");
// The start position of the drawing
myline.style.strokeDasharray = length;
// Hide the triangle by offsetting dash. Remove this line to show the triangle before scroll draw
myline.style.strokeDashoffset = length;
// Find scroll percentage on scroll (using cross-browser properties), and offset dash same amount as percentage scrolled
window.addEventListener("scroll", myFunction);
function myFunction() {
// What % down is it?
var scrollpercent = (document.body.scrollTop + document.documentElement.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight);
// Length to offset the dashes
var draw = length * scrollpercent;
// Reverse the drawing (when scrolling upwards)
myline.style.strokeDashoffset = length - draw;
//get point at length
endPoint = myline.getPointAtLength(draw);
circle.setAttribute("cx", endPoint.x);
circle.setAttribute("cy", endPoint.y);
}
body {
height: 2000px;
background: #f1f1f1;
}
#circle {
fill: red;
}
#mySVG {
position: absolute;
top: 15%;
width: 100%;
height: 1000px;
}
.st1 {
fill: none;
stroke-dashoffset: 3px;
stroke: grey;
stroke-width: 4;
stroke-miterlimit: 10;
stroke-dasharray: 20;
}
.st0 {
fill: none;
stroke-dashoffset: 3px;
stroke: red;
stroke-width: 5;
stroke-miterlimit: 10;
stroke-dasharray: 20;
}
<svg id="mySVG" viewBox="0 0 60 55" preserveAspectRatio="xMidYMin slice" style="width: 6%; padding-bottom: 42%; height: 1px; overflow: visible">
<path class="st1" stroke-dasharray="10,9" d="M 20 0 v 20 a 30 30 0 0 0 30 30 h 600 a 40 40 0 0 1 0 80 h -140 a 30 30 0 0 0 0 60 h 200 a 40 40 0 0 1 0 80 h -100 a 30 30 0 0 0 -30 30 v 20" /> Sorry, your browser does not support inline SVG.
</svg>
<svg id="mySVG" viewBox="0 0 60 55" preserveAspectRatio="xMidYMin slice" style="width: 6%; padding-bottom: 42%; height: 1px; overflow: visible">
<circle id="circle" cx="10" cy="10" r="10"/>
<path id="myline" class="st0" stroke-dasharray="10,9" d="M 20 0 v 20 a 30 30 0 0 0 30 30 h 600 a 40 40 0 0 1 0 80 h -140 a 30 30 0 0 0 0 60 h 200 a 40 40 0 0 1 0 80 h -100 a 30 30 0 0 0 -30 30 v 20" /> Sorry, your browser does not support inline SVG.
</svg>
What I want is no matter which size or shape the SVG <path> is
the growing line should be in the middle of the screen.
I tried changing the values of myline.style.strokeDashoffset = length //+newvalue - draw; and all but all it did was just ruins the consistency. so is there anyone who can help me solve this issue.?
Any help would be highly appreciatable.
(Update / New answer)
I think this is exactly what you want...
Update (added easing as OP wanted):
Made increase in length of #body smooth by adding transition: stroke-dashoffset ease-in-out 0.2s; to #body
Movement of #head can't be made smooth by adding transition: cx ease-in-out 0.2s, cy ease-in-out 0.2s; on #head because that will make it jump instead of moving along the path.
If you want #head to move smoothly along the track you'll have to do it by manually implementing an easing with js, which is a lot of work. So skipped that part.
let roadmapSvg = document.getElementById("roadmap-svg");
let track = document.getElementById("track");
let body = document.getElementById("body");
let head = document.getElementById("head");
let totalLength = track.getTotalLength();
let trackPoints = [];
let getTrackBounds = () => track.getBoundingClientRect();
let scaleFactor;
body.style.strokeDasharray = totalLength;
body.style.strokeDashoffset = totalLength;
function setScaleFactor(){
scaleFactor = roadmapSvg.getBoundingClientRect().width / roadmapSvg.viewBox.baseVal.width;
}
setScaleFactor();
function setTrackPoints(){
let divisions = 1000;
let unitLength = totalLength / divisions;
trackPoints = [];
for(let i=0; i < divisions; i++){
let length = unitLength * i;
let {x,y} = track.getPointAtLength(length);
trackPoints.push({x: x*scaleFactor, y: y*scaleFactor, length});
}
}
setTrackPoints();
function draw(){
let currentLength = getCurrentLength();
body.style.strokeDashoffset = totalLength - currentLength;
headPos = track.getPointAtLength(currentLength);
head.setAttribute("cx", headPos.x);
head.setAttribute("cy", headPos.y);
}
function getCurrentLength(){
let centreY = window.innerHeight / 2;
let trackBounds = getTrackBounds();
let currentY = centreY - trackBounds.y;
if(currentY < 0) return 0;
// if currentY is greater that track height, that means the user has scrolled pass the track (and the whole svg) in such case the animation should be completed i.e. the head should be at the final position i.e. at totalLength
if(currentY > trackBounds.height) return totalLength;
for(let point of trackPoints){
if(point.y >= currentY){
return point.length;
}
}
// (For safety) Sometimes none of the conditions match bcoz of low precision... Such situation only occurs a point very close to total length... Thus...
return totalLength;
}
document.addEventListener("scroll", draw);
window.addEventListener("resize", () => {
setScaleFactor();
setTrackPoints();
draw();
});
body {
background: #f1f1f1;
margin: 0;
padding: 0 20%;
font-family: sans-serif;
}
#roadmap-svg{
display: block;
max-width: 600px;
margin: 20px auto;
overflow: visible;
}
#roadmap-svg #head{
fill: red;
}
#roadmap-svg #track{
fill: none;
stroke-dashoffset: 3px;
stroke: grey;
stroke-width: 4;
stroke-miterlimit: 10;
stroke-dasharray: 20;
}
#roadmap-svg #body{
fill: none;
stroke-dashoffset: 3px;
stroke: red;
stroke-width: 5;
stroke-miterlimit: 10;
stroke-dasharray: 20;
transition: stroke-dashoffset ease-in-out 0.2s;
}
.center-line{
position: fixed;
left: 0;
right: 0;
top: 50%;
border-top: 1px solid red;
background-color: rgba(255,255,255,0.9);
}
<div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempora in eaque rem eligendi corrupti voluptate, maxime cum cumque, eius delectus minus neque, dolorem optio cupiditate ratione! Excepturi fugit culpa quo?
Cum optio error ex voluptatem rerum eius sunt, nemo necessitatibus, exercitationem voluptatum illum, rem quibusdam accusamus deserunt sed. Iste odio obcaecati enim voluptate temporibus ab illo maxime et sit minima.
Odio ut dignissimos sed dicta recusandae esse, at molestiae quibusdam, consequatur aspernatur facilis, perferendis voluptatum adipisci. Dolores molestiae quos, doloribus excepturi officiis laborum ex officia reprehenderit esse perspiciatis alias itaque.
Delectus illum, asperiores at a ab quibusdam corporis necessitatibus. Libero eos vero blanditiis modi cum rem maxime delectus quisquam, facilis saepe sed eius corrupti nobis sunt, unde obcaecati commodi velit.
Saepe adipisci consectetur blanditiis quos enim praesentium, at magnam quibusdam nisi! Dolore, esse beatae! Enim, quam cum, qui voluptates fugiat, nihil mollitia possimus doloremque porro aspernatur nesciunt velit. Cum, adipisci?
Dolores doloribus nihil delectus consequuntur id assumenda tempora, illum, earum ab quasi quaerat sequi et hic veniam excepturi eligendi quod perspiciatis voluptatem ratione reprehenderit! Corrupti minima facilis soluta adipisci animi!
Iure, sed exercitationem. Quidem assumenda omnis dicta ducimus sunt, quibusdam excepturi molestias cumque! Illum ipsum perferendis dicta optio eum consequuntur soluta, corrupti nostrum est sed quaerat voluptates dolores perspiciatis? Ex!
Consequatur corporis ratione beatae. Magni amet doloribus deserunt, accusamus suscipit earum accusantium perferendis adipisci inventore, ab commodi odio necessitatibus aut omnis. Et quisquam esse deleniti, reprehenderit nihil optio aperiam fugit.
Aliquid error voluptatibus, quis quo eveniet nulla corrupti veniam culpa voluptas possimus tenetur nisi recusandae quae modi, animi dolores. Provident saepe nobis quos tenetur, veritatis laborum cupiditate molestias fugit consectetur.
A, perspiciatis illo sequi non eos facere temporibus dignissimos blanditiis ipsum harum eius culpa adipisci est ab nobis saepe mollitia quis laboriosam tenetur, repellat molestias. Quos ipsa magni dolores rerum.</div>
<svg id="roadmap-svg" viewBox="0 0 760 300">
<path id="track" stroke-dasharray="10,9" d="M 20 0 v 20 a 30 30 0 0 0 30 30 h 600 a 40 40 0 0 1 0 80 h -140 a 30 30 0 0 0 0 60 h 200 a 40 40 0 0 1 0 80 h -100 a 30 30 0 0 0 -30 30 v 20" />
<path id="body" stroke-dasharray="10,9" d="M 20 0 v 20 a 30 30 0 0 0 30 30 h 600 a 40 40 0 0 1 0 80 h -140 a 30 30 0 0 0 0 60 h 200 a 40 40 0 0 1 0 80 h -100 a 30 30 0 0 0 -30 30 v 20" />
<circle id="head" cx="10" cy="10" r="10"/>
</svg>
<div class="center-line">Center Line</div>
<div>Lorem ipsum dolor sit amet consectetur adipisicing elit. Sapiente officia saepe facilis? Cupiditate rem vel, quaerat ratione ipsam magnam fugiat praesentium incidunt! Eveniet cum officia impedit obcaecati id animi rerum?
Non beatae inventore quos optio temporibus ratione doloremque ullam animi dolore reiciendis sint, esse consequatur asperiores assumenda repudiandae obcaecati ab quas molestias harum eveniet amet natus ea? Ipsum, dolore suscipit.
Explicabo assumenda minus, reprehenderit modi, laboriosam placeat saepe at repudiandae perferendis fugit asperiores itaque. Vero fugiat voluptas asperiores dolores dolorum quis ipsa sapiente deleniti odio, deserunt, iure voluptates. Error, tempore.
Doloribus nesciunt praesentium ad aut minus aliquam aspernatur quas qui incidunt sunt, maxime tempora facilis, cum assumenda. Dolorum a tempore itaque impedit, ad, corporis tenetur enim nulla quas, harum fuga!
Quae repellat, obcaecati voluptate inventore quidem, labore quo corporis repudiandae, vel doloremque perferendis numquam aliquam nisi? Vel architecto ullam fugiat error corrupti? Cumque amet illo, possimus assumenda eos unde deleniti.
Enim tenetur possimus a neque, voluptatum reprehenderit, cum magni blanditiis quam atque dolorum veniam eveniet repellendus. Modi quibusdam maxime corrupti harum! Ullam vitae assumenda laboriosam nam officia eaque. Totam, dolorem.
Ad sapiente itaque blanditiis, sint iusto nemo laborum corrupti cupiditate obcaecati quam ipsa quis perferendis vitae enim atque ex a ratione. Doloribus aspernatur id ipsa recusandae labore aliquid, totam aperiam?
Recusandae delectus quidem, aspernatur nulla expedita accusantium quod praesentium inventore qui, pariatur ullam maxime! Numquam, sed sequi rem voluptates asperiores qui, culpa nesciunt magnam, quas doloribus praesentium et adipisci tempora.
Veniam, placeat vel nesciunt recusandae voluptates laboriosam totam doloremque saepe. Nam quo similique vero esse possimus architecto officiis harum ratione perspiciatis dolor ut, molestias odit consequatur quam asperiores? Id, quasi!
Ex expedita impedit aliquam et commodi voluptatibus, consequatur voluptate ea explicabo deserunt. Sapiente quo consequuntur enim dolores ea officia. Inventore ipsa dignissimos iste qui magnam reiciendis eveniet optio laudantium fugiat!</div>
I tried several ways to get point moving
inside the box
not sliding away until end
with minimal changes to original.
I made the following changes (working example below):
divider for percent I put 2000 (equal to 2000px, the containing body height)
I multiplied the scroll amount from top by 18 (suitable value was a compromise whether the top or bottom becomes behaving wierdly)
then finally I checked the percent value is not greater to one (it started to eat the 'worm' from the other end).
That's it! Maybe not the fanciest, but works.
Problematic here is the svg line is not linear from top to down, so direct element related values could not be chosen, or I at least did not find some. Thus, I ended up to simple solution and playing with parameters.
// Get the id of the <path> element and the length of <path>
var myline = document.getElementById("myline");
var length = myline.getTotalLength();
circle = document.getElementById("circle");
// The start position of the drawing
myline.style.strokeDasharray = length;
// Hide the triangle by offsetting dash. Remove this line to show the triangle before scroll draw
myline.style.strokeDashoffset = length;
// Find scroll percentage on scroll (using cross-browser properties), and offset dash same amount as percentage scrolled
window.addEventListener("scroll", myFunction);
function myFunction() {
// What % down is it?
var scrollpercent = (document.documentElement.scrollTop * 18) / 2000;
if (scrollpercent > 1) scrollpercent = 1;
var draw = length * scrollpercent;
// Reverse the drawing (when scrolling upwards)
myline.style.strokeDashoffset = length - draw;
//get point at length
endPoint = myline.getPointAtLength(draw);
circle.setAttribute("cx", endPoint.x);
circle.setAttribute("cy", endPoint.y);
}
body {
height: 2000px;
background: #f1f1f1;
}
#circle {
fill: red;
}
#mySVG {
position: absolute;
top: 15%;
width: 100%;
height: 1000px;
}
.st1 {
fill: none;
stroke-dashoffset: 3px;
stroke: grey;
stroke-width: 4;
stroke-miterlimit: 10;
stroke-dasharray: 20;
}
.st0 {
fill: none;
stroke-dashoffset: 3px;
stroke: red;
stroke-width: 5;
stroke-miterlimit: 10;
stroke-dasharray: 20;
}
<svg id="mySVG" viewBox="0 0 60 55" preserveAspectRatio="xMidYMin slice" style="width: 6%; padding-bottom: 42%; height: 1px; overflow: visible">
<path class="st1" stroke-dasharray="10,9" d="M 20 0 v 20 a 30 30 0 0 0 30 30 h 600 a 40 40 0 0 1 0 80 h -140 a 30 30 0 0 0 0 60 h 200 a 40 40 0 0 1 0 80 h -100 a 30 30 0 0 0 -30 30 v 20" /> Sorry, your browser does not support inline SVG.
</svg>
<svg id="mySVG" viewBox="0 0 60 55" preserveAspectRatio="xMidYMin slice" style="width: 6%; padding-bottom: 42%; height: 1px; overflow: visible">
<circle id="circle" cx="10" cy="10" r="10"/>
<path id="myline" class="st0" stroke-dasharray="10,9" d="M 20 0 v 20 a 30 30 0 0 0 30 30 h 600 a 40 40 0 0 1 0 80 h -140 a 30 30 0 0 0 0 60 h 200 a 40 40 0 0 1 0 80 h -100 a 30 30 0 0 0 -30 30 v 20" /> Sorry, your browser does not support inline SVG.
</svg>
As per the comments and few modification to css and svg code, i was able to get the line to the center of the page, please check the working example below:
// Get the id of the <path> element and the length of <path>
var myline = document.getElementById("myline");
var length = myline.getTotalLength();
circle = document.getElementById("circle");
// The start position of the drawing
myline.style.strokeDasharray = length;
// Hide the triangle by offsetting dash. Remove this line to show the triangle before scroll draw
myline.style.strokeDashoffset = length;
// Find scroll percentage on scroll (using cross-browser properties), and offset dash same amount as percentage scrolled
window.addEventListener("scroll", myFunction);
function myFunction() {
// What % down is it?
var scrollpercent = (document.body.scrollTop + document.documentElement.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight);
// Length to offset the dashes
var draw = length * scrollpercent;
// Reverse the drawing (when scrolling upwards)
myline.style.strokeDashoffset = length - draw;
//get point at length
endPoint = myline.getPointAtLength(draw);
circle.setAttribute("cx", endPoint.x);
circle.setAttribute("cy", endPoint.y);
}
body {
margin: 0;
height: 1000px;
background: #f1f1f1;
}
#circle {
fill: red;
}
#mySVG {
top: 15%;
position: absolute;
width: 100%;
}
.st1 {
fill: none;
stroke-dashoffset: 1;
stroke: grey;
stroke-width: .5;
stroke-miterlimit: 1;
stroke-dasharray: 2;
}
.st0 {
fill: none;
stroke-dashoffset: 3px;
stroke: red;
stroke-width: 1;
stroke-miterlimit: 1;
stroke-dasharray: 2;
}
.grid {
position: fixed;
width: 1px;
height: 100%;
background: blue;
left: 50%;
top: 0;
}
<div class="grid"></div>
<svg id="mySVG" viewBox="0 0 200 72" preserveAspectRatio="xMidYMin slice">
<path class="st1" stroke-dasharray="10,9" d="m 0,5 0,4 c 0,3 2,6 5,6 l 108,0 c 4,0 7,4 7,8 0,4 -3,7 -7,7 l -25,0 c -3,0 -6,3 -6,6 0,3 3,6 6,6 l 35,0 c 4,0 7,4 7,8 0,4 -3,7 -7,7 l -18,0 c -3,0 -5,3 -5,6 l 0,4" />
</svg>
<svg id="mySVG" viewBox="0 0 200 72" preserveAspectRatio="xMidYMin slice">
<circle id="circle" cx="0" cy="3" r="2" />
<path id="myline" class="st0" stroke-dasharray="10,9" d="m 0,5 0,4 c 0,3 2,6 5,6 l 108,0 c 4,0 7,4 7,8 0,4 -3,7 -7,7 l -25,0 c -3,0 -6,3 -6,6 0,3 3,6 6,6 l 35,0 c 4,0 7,4 7,8 0,4 -3,7 -7,7 l -18,0 c -3,0 -5,3 -5,6 l 0,4" />
</svg>
I'd choose a different approach. I understand you want to find the point on the path that is closest to the middle of the screen. Let's do that:
Find the coordinates of the middle of the screen and convert them to the coordinate system of the path. The SVG API has two functions for that: .getScreenCTM() and SVGPoint.matrixTransform().
Find the point on the path (and its distance along the path) that is nearest to these coordinates. There is a bit of math and a search algorithm involved to do that. Mike Bostock has shown such an algorithm, and it's used here. Note that his function is open to a bit of tweaking (the precision parameter).
Use these data to draw the circle and the dashoffset.
It is probably a good idea to refine this by introducing a throttle for the scroll events (second variant) and then set CSS transitions to avoid visible jumps.
Transitions for the circle positioning only work with a CSS transform property. (My solution does not neccessarily move the circle along the path while transitioning. It's possible to achieve that, but goes far beyond the scope of this answer.)
var mySVG = document.getElementById("mySVG");
// Get the id of the <path> element and the length of <path>
var myline = document.getElementById("myline");
var pathLength = myline.getTotalLength();
circle = document.getElementById("circle");
// The start position of the drawing
myline.style.strokeDasharray = pathLength;
// Hide the triangle by offsetting dash. Remove this line to show the triangle before scroll draw
myline.style.strokeDashoffset = pathLength;
// throttled scroll event listener
function throttle(ms, callback) {
var timer, lastCall=0;
return function() {
var now = new Date().getTime(),
diff = now - lastCall;
if (diff >= ms) {
lastCall = now;
callback();
}
};
}
window.addEventListener("scroll", throttle(100, myFunction));
// one initial execution
myFunction();
function myFunction() {
var center = mySVG.createSVGPoint();
// middle of browser viewport
center.x = window.innerWidth / 2;
center.y = window.innerHeight / 2;
// transform to path coordinate system
var matrix = myline.getScreenCTM().inverse();
center = center.matrixTransform(matrix);
//find nearest length on path
var draw = getNearestLength(center);
// Reverse the drawing (when scrolling upwards)
myline.style.strokeDashoffset = -draw - pathLength;
//get point at length
endPoint = myline.getPointAtLength(draw);
circle.style.transform = "translate(" + endPoint.x + "px, " + endPoint.y + "px)";
}
function getNearestLength(point) {
var precision = 8,
best,
bestLength,
bestDistance = Infinity;
// linear scan for coarse approximation
for (var scan, scanLength = 0, scanDistance; scanLength <= pathLength; scanLength += precision) {
if ((scanDistance = distance2(scan = myline.getPointAtLength(scanLength))) < bestDistance) {
best = scan, bestLength = scanLength, bestDistance = scanDistance;
}
}
// binary search for precise estimate
precision /= 2;
while (precision > 0.5) {
var before,
after,
beforeLength,
afterLength,
beforeDistance,
afterDistance;
if ((beforeLength = bestLength - precision) >= 0 && (beforeDistance = distance2(before = myline.getPointAtLength(beforeLength))) < bestDistance) {
best = before, bestLength = beforeLength, bestDistance = beforeDistance;
} else if ((afterLength = bestLength + precision) <= pathLength && (afterDistance = distance2(after = myline.getPointAtLength(afterLength))) < bestDistance) {
best = after, bestLength = afterLength, bestDistance = afterDistance;
} else {
precision /= 2;
}
}
return bestLength;
function distance2(p) {
var dx = p.x - point.x,
dy = p.y - point.y;
return dx * dx + dy * dy;
}
}
body {
height: 2000px;
background: #f1f1f1;
}
#circle {
fill: red;
}
#mySVG {
position: absolute;
top: 15%;
width: 100%;
height: 1000px;
}
.st1 {
fill: none;
stroke-dashoffset: 3px;
stroke: grey;
stroke-width: 4;
stroke-miterlimit: 10;
stroke-dasharray: 20;
}
.st0 {
fill: none;
stroke-dashoffset: 3px;
stroke: red;
stroke-width: 5;
stroke-miterlimit: 10;
stroke-dasharray: 20;
transition: stroke-dashoffset 0.2s;
}
#circle {
transform: translate(10px, 10px);
transition: transform 0.2s;
}
<svg id="mySVG" viewBox="0 0 60 55" preserveAspectRatio="xMidYMin slice" style="width: 6%; padding-bottom: 42%; height: 1px; overflow: visible">
<path class="st1" stroke-dasharray="10,9" d="M 20 0 v 20 a 30 30 0 0 0 30 30 h 600 a 40 40 0 0 1 0 80 h -140 a 30 30 0 0 0 0 60 h 200 a 40 40 0 0 1 0 80 h -100 a 30 30 0 0 0 -30 30 v 20" /> Sorry, your browser does not support inline SVG.
</svg>
<svg id="mySVG" viewBox="0 0 60 55" preserveAspectRatio="xMidYMin slice" style="width: 6%; padding-bottom: 42%; height: 1px; overflow: visible">
<circle id="circle" cx="0" cy="0" r="10"/>
<path id="myline" class="st0" stroke-dasharray="10,9" d="M 20 0 v 20 a 30 30 0 0 0 30 30 h 600 a 40 40 0 0 1 0 80 h -140 a 30 30 0 0 0 0 60 h 200 a 40 40 0 0 1 0 80 h -100 a 30 30 0 0 0 -30 30 v 20" /> Sorry, your browser does not support inline SVG.
</svg>
Related
I have page where there is DIV and download button, the download button convert the div to pdf by JSPDF and downloads it, now the page contains 2 DIVs with the same size but different content, and I need the download button to create 2 pages PDF file and download it
the JS
$('#print').click(function (e) {
e.preventDefault();
let HTML_Width = $(".report").width();
let HTML_Height = $(".report").height();
let top_left_margin = 1;
let PDF_Width = HTML_Width + (top_left_margin * 2);
let PDF_Height = (PDF_Width * 1.5) + (top_left_margin * 2);
let canvas_image_width = HTML_Width;
let canvas_image_height = HTML_Height;
let totalPDFPages = Math.ceil(HTML_Height / PDF_Height) - 1;
html2canvas($(".report")[0]).then(function (canvas) {
let imgData = canvas.toDataURL("image/jpeg", 1.0);
let pdf = new jsPDF('p', 'pt', [PDF_Width, PDF_Height]);
pdf.addImage(imgData, 'JPG', top_left_margin, top_left_margin, canvas_image_width,
canvas_image_height, 'FAST');
for (let i = 1; i <= totalPDFPages; i++) {
pdf.addPage(PDF_Width, PDF_Height);
pdf.addImage(imgData, 'JPG', top_left_margin, -(PDF_Height * i) + (top_left_margin *
4), canvas_image_width, canvas_image_height);
}
pdf.save("Report.pdf");
$(".html-content").hide();
});
});
the first div selector '.report', and the second div selector '#report-two'
can anyone give me hand please?
You can try with:
page-break-before: always;
Example:
#media print {
.new-page {
page-break-before: always;
}
button {
display:none;
}
}
<div>
<h1>1 Page</h1>
<p>
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Porro unde
voluptas pariatur, sapiente voluptatem dignissimos ratione provident
tenetur exercitationem itaque suscipit incidunt excepturi iusto
consectetur odit reiciendis sunt quibusdam. Vitae?
</p>
</div>
<div class="new-page">
<h1>2 Page</h1>
<p>
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Porro unde
voluptas pariatur, sapiente voluptatem dignissimos ratione provident
tenetur exercitationem itaque suscipit incidunt excepturi iusto
consectetur odit reiciendis sunt quibusdam. Vitae?
</p>
</div>
<button onClick="window.print()">Print pages</button>
Otherwise if above not help try setting fixed height & width.
With JsPDF you need to have new jsPDF("p", "pt", "a4"); and 600px width and 840px height for A4 portrait orientation.
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>
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>
I have a game where users can chat. Every speech is appended to body as position absolute element with user x position as "left" CSS tag. They are also animated to go to top. I don't want them to have a colliding, vertically or horisontally.
Here's an example:
They don't have to be one on another, but they need to be "one after one"
I have tried a jquery each() and then remove 60 pixel from the current speech position. Here's my code:
var Speech = {
History: [],
New: function(user, text, int) {
var pos = getUserPosition(user).x + 18;
var speech = HTML.Speech.split('\n');
var maxInt = speech.length - 1;
if (int <= maxInt) {
var html = speech[int];
var random = Rand(10);
Speech.pushThemUp();
/** append here ect...
set left position...
$('#speech' + random).css("left", nLeft + "px"); **/
}
},
pushThemUp: function() {
$('.speech').each(function(i, obj) {
var newTop = parseInt($(this).css('top')) - 60;
$(this).css('top', newTop+'px');
});
},
Listener: function() {
var int = setInterval(function() {
$('.speech').each(function(i, obj) {
if(parseInt($(this).css('top')) < 0) {
$(this).remove();
} else {
var newTop = parseInt($(this).css('top')) - 10;
$(this).animate({'top': newTop+'px'});
}
});
}, 1000);
},
getHistory: function() {
return Speech.History;
}
};
Speech.Listener();
module.exports = Speech;
But it doesn't work. They can still have colliding like the example up.
How can I solve that?
Please note: in that example, Speech.Listener() wasn't called.
EDIT: finally, I think my current solution to loop over .speech class and then add top px is good, but why is it animated? Look at the gif, pushThemUp function don't have to animate the speech bubbles but directly edit position, how can I solve that?
I created a code snippet where messages appear and move upwards. When there's no space left, a scrollbar appears.
var box = document.getElementById("box");
var topp = 3;
function post(str) {
var obj = document.createElement("div");
obj.className = "chatObj";
obj.innerHTML = str;
box.appendChild(obj);
box.appendChild(document.createElement("br"));
var width = obj.clientWidth;
var height = obj.clientHeight;
obj.style.marginLeft = (box.clientWidth / 2 - width / 2) + "px";
var x = 15;
obj.style.top = (box.clientHeight - x - height) + "px";
var interval, ctop;
interval = setInterval(function() {
x += 4;
console.log(ctop, topp);
ctop = box.clientHeight - x - height;
obj.style.top = ctop + "px";
if (ctop <= topp) {
obj.style.top = topp + "px";
topp += height + 6;
clearInterval(interval);
}
}, 5);
}
setTimeout(function() {
post("Hi!");
}, 500);
setTimeout(function() {
post("Trollolo!");
}, 1500);
setTimeout(function() {
post("Lorem ipsum dolor sit amet, timeam aliquando ei cum, putent possim in usu, at causae pericula petentium has. In mea legere salutatus voluptaria. No vix ancillae accusata. Nec meis probo noster eu, ius no quas audire.<br><br>Qui quem nominavi ei. Pri nisl eirmod id. Has cetero vocent abhorreant no, at mei altera expetendis. Has id novum aeterno salutatus.<br><br>Prompta offendit et eos, eos an admodum comprehensam, ex velit doming dolorem mei. At has dolor alterum laoreet, id duo tollit libris contentiones. An mel recteque omittantur dissentiet, ex nam novum iuvaret. Per id alterum habemus gubergren.<br><br>Nulla possim mea in. Vis et postulant constituam. Viris vulputate vituperatoribus eu usu, wisi meis ex his. Prompta accumsan cum et, possim eligendi omittantur sed id. Eos ad nemore integre recusabo, agam doctus viderer ei pri, cu eius nonumes senserit vis. Qui iudico causae te.<br><br>Eam ne mandamus evertitur, case adversarium neglegentur duo ex, no cum nominati forensibus. Et vel putant deleniti. Illum aliquando voluptatibus per no, ei quo albucius phaedrum. Cu lorem appetere percipit sed, ubique epicuri ad eos, te eos diam nusquam persecuti. Eu qui meis illum eleifend, eam veniam vivendo no, nisl fierent in quo.");
}, 2500);
#box {
display: block;
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
background-color: tomato;
overflow: auto;
}
#box .chatObj {
position: absolute;
display: inline-block;
max-width: 80%;
background-color: white;
border-radius: 2px;
padding: 3px 6px;
margin: 3px 0;
box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.1);
font-family: Arial, sans-serif;
font-size: 14px;
}
#box .chatObj::after {
display: block;
position: absolute;
content: ".";
color: transparent;
height: 6px;
}
<div id="box"></div>
If you use position: relative and a wrapper element with display: block, you can get exactly what you want without having to worry about collision detection. The only thing you'll need to do is calculate the initial top value based on the players position minus the initial top position of the bubble (since using position: relative the bubbles will be added to the DOM bellow the last bubble).
See this simple jsfiddle. https://jsfiddle.net/straker/0n6jbupg/
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).