Is it possible for li elements animation from here:
http://jsfiddle.net/8XM3q/light/
to animate when there is show/hide function used instead of remove?
When i have changed "remove" to "hide" elements didn't move: http://jsfiddle.net/8XM3q/90/
I wanted to use this function for my content filtering animations - thats why i have to replace "remove" to "hide/show".
I'm not good at JS but i think that it counts all elements, even when they are hidden:
function createListStyles(rulePattern, rows, cols) {
var rules = [], index = 0;
for (var rowIndex = 0; rowIndex < rows; rowIndex++) {
for (var colIndex = 0; colIndex < cols; colIndex++) {
var x = (colIndex * 100) + "%",
y = (rowIndex * 100) + "%",
transforms = "{ -webkit-transform: translate3d(" + x + ", " + y + ", 0); transform: translate3d(" + x + ", " + y + ", 0); }";
rules.push(rulePattern.replace("{0}", ++index) + transforms);
}
}
var headElem = document.getElementsByTagName("head")[0],
styleElem = $("<style>").attr("type", "text/css").appendTo(headElem)[0];
if (styleElem.styleSheet) {
styleElem.styleSheet.cssText = rules.join("\n");
} else {
styleElem.textContent = rules.join("\n");
}
So my question is how to adapt that part of code to count only "show" (displayed) elements?
If you want to have the animation and still have all of the data then use detach() function instead of remove: jQuery - detach
And to count or select elements try to do this using css's class attached to each element.
I edited your jsFiddle:
http://jsfiddle.net/8XM3q/101/
notice that I changed this line:EDIT: http://jsfiddle.net/8XM3q/101/
$(this).closest("li").remove();
to this:
$(this).closest("li").hide("slow",function(){$(this).detach()});
This means hide the item, speed = slow, when done hiding remove it.
Hope this is what you meant.
EDIT: Included detach.
As per your comment:
I wanted to use this function for my content filtering animations -
thats why i have to replace "remove" to "hide/show" I don't want to
remove elements at all. Im sorry if I mislead You with my question.
What you can do is to use a cache to store the list-items as they are hidden when you do the content filtering. Later when you need to reset the entire list, you can replenish the items from the cache.
Relevant code fragment...
HTML:
...
<button class="append">Add new item</button>
<button class="replenish">Replenish from cache</button>
<div id="cache"></div>
JS:
...
$(this).closest("li").hide(600, function() {
$(this).appendTo($('#cache'));
});
...
$(".replenish").click(function () {
$("#cache").children().eq(0).appendTo($(".items")).show();
});
Demo Fiddle: http://jsfiddle.net/abhitalks/8XM3q/102/
Snippet:
$(function() {
$(document.body).on("click", ".delete", function (evt) {
evt.preventDefault();
$(this).closest("li").hide(600, function() {
$(this).appendTo($('#cache'));
});
});
$(".append").click(function () {
$("<li>New item <a href='#' class='delete'>delete</a></li>").insertAfter($(".items").children()[2]);
});
$(".replenish").click(function () {
$("#cache").children().eq(0).appendTo($(".items")).show();
});
// Workaround for Webkit bug: force scroll height to be recomputed after the transition ends, not only when it starts
$(".items").on("webkitTransitionEnd", function () {
$(this).hide().offset();
$(this).show();
});
});
function createListStyles(rulePattern, rows, cols) {
var rules = [], index = 0;
for (var rowIndex = 0; rowIndex < rows; rowIndex++) {
for (var colIndex = 0; colIndex < cols; colIndex++) {
var x = (colIndex * 100) + "%",
y = (rowIndex * 100) + "%",
transforms = "{ -webkit-transform: translate3d(" + x + ", " + y + ", 0); transform: translate3d(" + x + ", " + y + ", 0); }";
rules.push(rulePattern.replace("{0}", ++index) + transforms);
}
}
var headElem = document.getElementsByTagName("head")[0],
styleElem = $("<style>").attr("type", "text/css").appendTo(headElem)[0];
if (styleElem.styleSheet) {
styleElem.styleSheet.cssText = rules.join("\n");
} else {
styleElem.textContent = rules.join("\n");
}
}
createListStyles(".items li:nth-child({0})", 50, 3);
body { font-family: Arial; }
.items {
list-style-type: none; padding: 0; position: relative;
border: 1px solid black; height: 220px; overflow-y: auto; overflow-x: hidden;
width: 600px;
}
.items li {
height: 50px; width: 200px; line-height: 50px; padding-left: 20px;
border: 1px solid silver; background: #eee; box-sizing: border-box; -moz-box-sizing: border-box;
position: absolute; top: 0; left: 0;
-webkit-transition: all 0.2s ease-out; transition: all 0.2s ease-out;
}
div.cache { display: none; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="items">
<li>Monday delete
</li><li>Tuesday delete
</li><li>Wednesday delete
</li><li>Thursday delete
</li><li>Friday delete
</li><li>Saturday delete
</li><li>Sunday delete</li>
</ul>
<button class="append">Add new item</button>
<button class="replenish">Replenish from cache</button>
<div id="cache"></div>
EDIT: There is a simpler way without adding any classes, is to use the :visible selector
You need to understand a concept is Javascript, which is that functions are considered objects. You can pass a function to another function, or return a function from a function.
Let's check the documentation on jQuery for the hide function
.hide( duration [, easing ] [, complete ] )
It says that it accepts a function as an argument for complete, which is called when the hide animation is complete.
The function hide does not remove the element from the DOM but simply "hides" it as the name suggests. So what we want to do, is hide the element then when the animation of hiding is done, we add a class "removed" to the list element.
We will accomplish that by passing a function (complete argument) like so :
$(this).closest("li").hide(400, function() {
$(this).addClass('removed');
});
When you want to select the list elements that are not "removed", use this selector $('li:not(.removed)')
Related
I am writing some pure Javascript that requires me to add elements to a flex container dynamically row by row. To my surprise, My mouseover event propagate across the row and trigger the other children even though it shouldn't. Below is my code:
function drawChildren() {
var size = Math.floor(containerSize / childSize);
var counter = 1;
var parent = document.getElementById(parentId);
for(var rowCount = 1; rowCount <= size ; rowCount ++) {
var row = document.createElement('div');
row.id = `${parentId}-rowDiv-${rowCount} `;
row.setAttribute('style', `
height: ${childSize}px;
width: ${containerSize}px;
display: flex;
flex-direction:row; `);
for(var child = 1; child <= size ; child ++) {
var childDiv = document.createElement('div');
childDiv.id = `${parentId}-childDiv-${counter}`;
childDiv.setAttribute('style',`
height: ${childSize}px;
width: ${childSize}px;
background-color: ${getRandomColor()};`);
childDiv.addEventListener("mouseover", onMouseOver);
childDiv.addEventListener("mouseleave", onMouseLeave);
row.appendChild(childDiv);
counter ++;
}
parent.appendChild(row);
}
onmouseover , I called the function below:
function onMouseOver(e) {
e.stopPropagation();
document.getElementById(e.target.id).style.display = 'none';
console.log(e.target.id);
}
The problem is, whenever I mouseover on an object, it propagates across the row and fires the mouseover event for all the other items on the same row. It does fire one at a time also. I tried to stop propagation by adding the js stopPropagation() prop yet nothing change. Please what is causing this and how do I address it? Any help would be appreciated.
The JS logic works just fine after removing the syntax used for getting the variables for size and parentId (which I'm guessing is from JSP). May be the backtick (`) used is the issue.
OR
You are referring to the problem where hovering on first child of the row hides the entire row.
Here, display:none; will be the culprit and you can use visibility: hidden; instead.
display: none; will remove the element from the layout, freeing its space taken from the layout and thus, allowing the next element to take up its space.
In the question, hovering on 1st child frees the space which is now taken by the 2nd element. Since your mouse is still at the same position, it will now remove the 2nd element and the cycle goes so on.
visibility: hidden; only hides the element while retaining its space in the layout of the page.
Here's a working snippet of your code (with display: none; and visibility : hidden;):
var containerSize = 200,
childSize = 50;
function onMouseOverDisplay(e) {
e.stopPropagation();
document.getElementById(e.target.id).style.display = 'none';
console.log(e.target.id);
}
function onMouseOverVisibility(e) {
e.stopPropagation();
document.getElementById(e.target.id).style.visibility = 'hidden';
console.log(e.target.id);
}
function setAttr(elem, attrs) {
for (var attr in attrs) {
if (attrs.hasOwnProperty(attr)) {
elem.setAttribute(attr, attrs[attr]);
}
}
}
function drawChildren(parentId) {
var size = Math.floor(containerSize / childSize),
parent = document.getElementById(parentId),
counter = 1,
rowCount, childCount, row, childDiv;
for (rowCount = 1; rowCount <= size; rowCount++) {
row = document.createElement('div');
row.id = parentId + "-rowDiv-" + rowCount;
row.setAttribute('style', "height: " + childSize + "px; width: " + containerSize + "px; display: flex; flex-direction: row;");
for (childCount = 1; childCount <= size; childCount++) {
childDiv = document.createElement('div');
childDiv.id = parentId + "-childDiv-" + rowCount + "-" + childCount;
childDiv.setAttribute('style', "height: " + childSize + "px; width: " + childSize + "px; background-color: cyan; border: 1px solid red;");
if (parentId === 'tab-display') {
childDiv.addEventListener("mouseover", onMouseOverDisplay);
} else if (parentId === 'tab-visibility') {
childDiv.addEventListener("mouseover", onMouseOverVisibility);
}
// childDiv.addEventListener("mouseleave", onMouseLeave);
row.appendChild(childDiv);
counter++;
}
parent.appendChild(row);
}
}
drawChildren('tab-display');
drawChildren('tab-visibility');
<h2>Using Display None</h2>
<div id="tab-display"></div>
<h2>Using Visibilty Hidden</h2>
<div id="tab-visibility"></div>
I've made a JavaScript dropdown menu. Everything works fine, except the background image. I have the image set to change when the dropdown menu is expanded, which also works fine.
The issue is with the headers. Unless the header is set to display inline-block or inline, the menu won't expand. When set to inline-block or inline everything expands when you click on the box. But if you click on the header itself, it adds the padding and border around the header and ads in the background image from the div. How do you prevent this from happening?
<div class="panel">
<div class="collapse"><h2>Features</h2></div>
<div class="elements">
text<br>text<br>text
</div>
</div>
<style>
h2 {/*display: inline-block;*/
/*display: inline;*/
margin: 0px;
padding: 0px 0px 0px 0px;
color: #ffffff;
text-align: center;
text-transform: uppercase;}
.expand,
.collapse {cursor: pointer;
background-position: center right;
background-repeat: no-repeat;
background-color: #000033;
border: 2px solid #990044;
color: #ffffff;
padding: 10px 0px;
text-align: center;}
.collapse {background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAABHUlEQVRIS+3USw6CMBAA0BYNutOjcAQ9iXHjhoXhBt4AEmwwbvQm6g04ii4hCLaNNRUp/dDgQllRPvMy05lC0PMFe/bAH7Re8R8qaYySTZGPoyBYXm3WMQwP04Gbhfd8FJDYtKTxNjkCCBf4Ni3y0dwWSrChm51wXI/FhjHaRXix5rKygtYwGr4C1QUitPdKUJ7xemILbcJw7JsDnBktqU20DfP9VfoaCxuoDCPJvc1hF1QF+wBNy6uKNYK6qA4mBFVRXawVlKHkPTfUbKJo65NuFJ1W0sNb1EjPgOQEUcakGbJIApRPQpoZ+1iaoQKqjCln2IJqYdpgrZGArEGaGke5pPzPZE/Juq0bjbtU9KPpc6MMTTGjPeyCfQV8AK4c2lwJRjQ3AAAAAElFTkSuQmCC);}
.expand {background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAABD0lEQVRIS+3Wyw2CQBAG4F1Q8KadSAnYgR0YL164SCWYKDHxonZgB1ICdiI3QR6yCgSVXXaAkGDkwgXyZf6dYcCo5Qu37KFugIaxH5FkdH1+hSYErpBgPck9E8j35AkUBYE5TEkqs6EoN1iApWmCUC6QgYHRUpCCOYk0zDUNV6VMkIYJSFAJFKLQim8glAqyME1b2AQ0zZ0CRQtBHiyNEop+gRCsCvoGVsGgaAbWwSDoE2wC40VxkxgPiteb7QFhPMsNsEPmLG196DZgolF0fFXYd614M47jhxvBCtEIXfy7rGZnKEq3k4jEZd3KPhMhcxqgYBV4gylZZaXf0qqR0t77g00n2pG/tjpl/37TPACe/d8VUJ3+EgAAAABJRU5ErkJggg==);}
.elements {background-color: #ccd9ff;
overflow: hidden;}
</style>
<script>
function aaManageEvent (eventObj, event, eventHandler) {
if (eventObj.addEventListener) {eventObj.addEventListener (event, eventHandler, false);}
else if (eventObj.attachEvent) {event = "on" + event; eventObj.attachEvent (event, eventHandler);}
}
window.onload = function () {
var divs = document.getElementsByTagName ("div");
for (var i = 0; i < divs.length; i++) {
if (divs[i].className == "collapse") {
aaManageEvent (divs [i], "click", spring.expandOrCollapse);
}
else if (divs[i].className == "elements") {
var height = divs [i].offsetHeight;
divs [i] .height = height;
if (divs [i] .id == "") divs [i].id = "div" + i;
divs [i].style.height = "0";
}
}
}
var spring = {
// adjust height
adjustItem : function (val, newItem) {
document.getElementById (newItem).style.height = val + "px";
},
// check if expand or collapse
expandOrCollapse : function (evnt) {
evnt = evnt ? evnt : window.event;
var target = evnt.target ? evnt.target : evnt.srcElement;
if (target.className == "collapse") spring.expand (target);
else spring.collapse (target);
},
// Expand Panel
expand : function (target) {
target.className = 'expand';
var children = target.parentNode.childNodes, panel;
for (var i = 0; i < children.length; i++) {
if (children [i].className == "elements") {
panel = children [i]; break;
}
}
var height = panel.height, incr = height / 20;
for (var i=0; i < 20; i++) {
var val = (i + 1) * incr;
var func = "spring.adjustItem (" + val + ", '" + panel.id + "')";
setTimeout (func, (i + 1) * 30);
}
},
// Collapse Panel
collapse : function (target) {
target.className = "collapse";
var children = target.parentNode.childNodes, panel;
for (var i = 0; i < children.length; i++) {
if (children [i].className == "elements") {
panel = children [i]; break;
}
}
var height = panel.height, decr = height / 20;
for (var i = 0; i < 20; i++) {
var val = height - (decr * (i + 1));;
var func = "spring.adjustItem (" + val + ", '" + panel.id + "')";
setTimeout (func, (i + 1) * 30);
}
}
};
</script>
When I click on the div, the dropdown works. But when I click on the header, I see an error in the browser console.
I think because when clicking on the <h2>Features</h2> element, the click event bubbles up to the <div class="collapse">, making the var target in this line not the <div class="collapse"> but the <h2>:
var target = evnt.target ? evnt.target : evnt.srcElement;
A possible solution to fix this is for example to add an id to this line:
<div id="header" class="collapse"><h2>Features</h2></div>
Then you can directly get that div by id and change the classname.
I've adjusted your expandOrCollapse function to make it toggle based on the classname from the div with id="header".
For example:
function aaManageEvent (eventObj, event, eventHandler) {
if (eventObj.addEventListener) {eventObj.addEventListener (event, eventHandler, false);}
else if (eventObj.attachEvent) {event = "on" + event; eventObj.attachEvent (event, eventHandler);}
}
window.onload = function () {
var divs = document.getElementsByTagName ("div");
for (var i = 0; i < divs.length; i++) {
if (divs[i].className == "collapse") {
aaManageEvent (divs [i], "click", spring.expandOrCollapse);
}
else if (divs[i].className == "elements") {
var height = divs [i].offsetHeight;
divs [i] .height = height;
if (divs [i] .id == "") divs [i].id = "div" + i;
divs [i].style.height = "0";
}
}
}
var spring = {
// adjust height
adjustItem : function (val, newItem) {
document.getElementById (newItem).style.height = val + "px";
},
// check if expand or collapse
expandOrCollapse : function (evnt) {
var header = document.getElementById('header');
if (header.className === "collapse") {
spring.expand(header);
} else {
spring.collapse(header);
}
},
// Expand Panel
expand : function (target) {
target.className = 'expand';
var children = target.parentNode.childNodes, panel;
for (var i = 0; i < children.length; i++) {
if (children [i].className == "elements") {
panel = children [i]; break;
}
}
var height = panel.height, incr = height / 20;
for (var i=0; i < 20; i++) {
var val = (i + 1) * incr;
var func = "spring.adjustItem (" + val + ", '" + panel.id + "')";
setTimeout (func, (i + 1) * 30);
}
},
// Collapse Panel
collapse : function (target) {
target.className = "collapse";
var children = target.parentNode.childNodes, panel;
for (var i = 0; i < children.length; i++) {
if (children [i].className == "elements") {
panel = children [i]; break;
}
}
var height = panel.height, decr = height / 20;
for (var i = 0; i < 20; i++) {
var val = height - (decr * (i + 1));;
var func = "spring.adjustItem (" + val + ", '" + panel.id + "')";
setTimeout (func, (i + 1) * 30);
}
}
};
h2 {/*display: inline-block;*/
/*display: inline;*/
margin: 0px;
padding: 0px 0px 0px 0px;
color: #ffffff;
text-align: center;
text-transform: uppercase;}
.expand,
.collapse {cursor: pointer;
background-position: center right;
background-repeat: no-repeat;
background-color: #000033;
border: 2px solid #990044;
color: #ffffff;
padding: 10px 0px;
text-align: center;}
.collapse {background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAABHUlEQVRIS+3USw6CMBAA0BYNutOjcAQ9iXHjhoXhBt4AEmwwbvQm6g04ii4hCLaNNRUp/dDgQllRPvMy05lC0PMFe/bAH7Re8R8qaYySTZGPoyBYXm3WMQwP04Gbhfd8FJDYtKTxNjkCCBf4Ni3y0dwWSrChm51wXI/FhjHaRXix5rKygtYwGr4C1QUitPdKUJ7xemILbcJw7JsDnBktqU20DfP9VfoaCxuoDCPJvc1hF1QF+wBNy6uKNYK6qA4mBFVRXawVlKHkPTfUbKJo65NuFJ1W0sNb1EjPgOQEUcakGbJIApRPQpoZ+1iaoQKqjCln2IJqYdpgrZGArEGaGke5pPzPZE/Juq0bjbtU9KPpc6MMTTGjPeyCfQV8AK4c2lwJRjQ3AAAAAElFTkSuQmCC);}
.expand {background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAABD0lEQVRIS+3Wyw2CQBAG4F1Q8KadSAnYgR0YL164SCWYKDHxonZgB1ICdiI3QR6yCgSVXXaAkGDkwgXyZf6dYcCo5Qu37KFugIaxH5FkdH1+hSYErpBgPck9E8j35AkUBYE5TEkqs6EoN1iApWmCUC6QgYHRUpCCOYk0zDUNV6VMkIYJSFAJFKLQim8glAqyME1b2AQ0zZ0CRQtBHiyNEop+gRCsCvoGVsGgaAbWwSDoE2wC40VxkxgPiteb7QFhPMsNsEPmLG196DZgolF0fFXYd614M47jhxvBCtEIXfy7rGZnKEq3k4jEZd3KPhMhcxqgYBV4gylZZaXf0qqR0t77g00n2pG/tjpl/37TPACe/d8VUJ3+EgAAAABJRU5ErkJggg==);}
.elements {background-color: #ccd9ff;
overflow: hidden;}
<div class="panel">
<div id="header" class="collapse"><h2>Features</h2></div>
<div class="elements">
text<br>text<br>text
</div>
</div>
Edit your declaration block of h2 as shown below. This will solve your problem.
h2 {
display: inline-block;
margin: 0px;
padding: 0px 0px 0px 0px;
color: #ffffff;
text-align: center;
text-transform: uppercase;
pointer-events: none; // this line solves your problem.
}
CSS property pointer-events let you control under what circumstances an element can become the target of mouse events. when you set it to none, the element will never be the target of mouse events. So, the click event passed on to its descendant elements (here the box).
I am trying to get three squares (put ramdomly) on page to disappear on click BUt for a reason I don't understand, some of them (usually the 2nd or 3rd one) reappear elsewhere on the page when I click on them.
They should just disappear.
I made a jsffidle: https://jsfiddle.net/DTcHh/9949/
The code:
HTML
<div class="container">
<div id="square-zone">
<!--
here appear the squares
-->
</div>
</div>
Javascript
//randomly place squares
$(function() {
var numInfoSquares = 3;
var $squareZone = $("#square-zone");
var $toAppend = $('<div class="info-square"><span class="square" data-toggle="modal"></span></div>');
for (var c = 0; c < numInfoSquares; c++) {
$squareZone.append(
$toAppend.clone()
.find('.square').attr("data-target", "#myInfoModal" + (c + 1))
.end()
);
};
// place info squares randomly on the page
function getRandomInt(min, max) {
return Math.random() * (max - min + 1) + min;
}
$(".info-square").each(function () {
var topPosition = getRandomInt(8, 70);
var leftPosition = getRandomInt(8, 92);
$(this).css({
"top": topPosition+"%",
"left": leftPosition+"%"
});
});
// clicked squares disappears on click
$(".info-square").click(function () {
$(this).css("display","none");
$(this).next().css("display","block");
});
});
CSS
body {
margin: 10px;
}
#square-zone > div { position: absolute; }
.info-square > span {
display: inline-block;
cursor: pointer;
background: url(http://cliparts.co/cliparts/ATb/jRa/ATbjRan5c.png) no-repeat center center;
width: 30px;
height: 30px;
}
How can I prevent the squares to reappear ?
Well just remove that line from your code which makes it appear again
$(this).next().css("display","block");
// clicked squares disappears on click
$(".info-square").click(function () {
$(this).css("display","none");
//$(this).next().css("display","block");
});
Updated jsfiddle with commented line https://jsfiddle.net/DTcHh/9950/
that is because of $(this).next().css("display","block");.
Assume you are clicking on 2nd element then it is hidden, then you are clicking on the first element now that is hidden but when the above line is executed it will display the second element back as it is the next sibling of the first info-square
//randomly place squares
$(function() {
var numInfoSquares = 3;
var $squareZone = $("#square-zone");
var $toAppend = $('<div class="info-square"><span class="square" data-toggle="modal"></span></div>');
for (var c = 0; c < numInfoSquares; c++) {
$squareZone.append(
$toAppend.clone()
.find('.square').attr("data-target", "#myInfoModal" + (c + 1))
.end()
);
};
// place info squares randomly on the page
function getRandomInt(min, max) {
return Math.random() * (max - min + 1) + min;
}
$(".info-square").each(function() {
var topPosition = getRandomInt(8, 70);
var leftPosition = getRandomInt(8, 92);
$(this).css({
"top": topPosition + "%",
"left": leftPosition + "%"
});
});
// clicked squares disappears on click
$(".info-square").click(function() {
$(this).css("display", "none");
//$(this).next().css("display", "block"); this displayes the next element back if it was already hidden
});
});
/* Latest compiled and minified CSS included as External Resource*/
body {
margin: 10px;
}
#square-zone > div {
position: absolute;
}
.info-square > span {
display: inline-block;
cursor: pointer;
background: url(http://cliparts.co/cliparts/ATb/jRa/ATbjRan5c.png) no-repeat center center;
width: 30px;
height: 30px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="container">
<div id="square-zone">
<!--
here appear the squares
-->
</div>
</div>
Alternately, changing $(this).css("display","none"); to $(this).css("visibility","hidden"); also solves it.
Here's the jsfiddle
PURE JS ONLY PLEASE - NO JQUERY
I have a div with overflow scroll, the window (html/body) never overflows itself.
I have a list of anchor links and want to scroll to a position when they're clicked.
Basically just looking for anchor scrolling from within a div, not window.
window.scrollTo etc. don't work as the window never actually overflows.
Simple test case http://codepen.io/mildrenben/pen/RPyzqm
JADE
nav
a(data-goto="#1") 1
a(data-goto="#2") 2
a(data-goto="#3") 3
a(data-goto="#4") 4
a(data-goto="#5") 5
a(data-goto="#6") 6
main
p(data-id="1") 1
p(data-id="2") 2
p(data-id="3") 3
p(data-id="4") 4
p(data-id="5") 5
p(data-id="6") 6
SCSS
html, body {
width: 100%;
height: 100%;
max-height: 100%;
}
main {
height: 100%;
max-height: 100%;
overflow: scroll;
width: 500px;
}
nav {
background: red;
color: white;
position: fixed;
width: 50%;
left: 50%;
}
a {
color: white;
cursor: pointer;
display: block;
padding: 10px 20px;
&:hover {
background: lighten(red, 20%);
}
}
p {
width: 400px;
height: 400px;
border: solid 2px green;
padding: 30px;
}
JS
var links = document.querySelectorAll('a'),
paras = document.querySelectorAll('p'),
main = document.querySelector('main');
for (var i = 0; i < links.length; i++) {
links[i].addEventListener('click', function(){
var linkID = this.getAttribute('data-goto').slice(1);
for (var j = 0; j < links.length; j++) {
if(linkID === paras[j].getAttribute('data-id')) {
window.scrollTo(0, paras[j].offsetTop);
}
}
})
}
PURE JS ONLY PLEASE - NO JQUERY
What you want is to set the scrollTop property on the <main> element.
var nav = document.querySelector('nav'),
main = document.querySelector('main');
nav.addEventListener('click', function(event){
var linkID,
scrollTarget;
if (event.target.tagName.toUpperCase() === "A") {
linkID = event.target.dataset.goto.slice(1);
scrollTarget = main.querySelector('[data-id="' + linkID + '"]');
main.scrollTop = scrollTarget.offsetTop;
}
});
You'll notice a couple of other things I did different:
I used event delegation so I only had to attach one event to the nav element which will more efficiently handle clicks on any of the links.
Likewise, instead of looping through all the p elements, I selected the one I wanted using an attribute selector
This is not only more efficient and scalable, it also produces shorter, easier to maintain code.
This code will just jump to the element, for an animated scroll, you would need to write a function that incrementally updates scrollTop after small delays using setTimeout.
var nav = document.querySelector('nav'),
main = document.querySelector('main'),
scrollElementTo = (function () {
var timerId;
return function (scrollWithin, scrollTo, pixelsPerSecond) {
scrollWithin.scrollTop = scrollWithin.scrollTop || 0;
var pixelsPerTick = pixelsPerSecond / 100,
destY = scrollTo.offsetTop,
direction = scrollWithin.scrollTop < destY ? 1 : -1,
doTick = function () {
var distLeft = Math.abs(scrollWithin.scrollTop - destY),
moveBy = Math.min(pixelsPerTick, distLeft);
scrollWithin.scrollTop += moveBy * direction;
if (distLeft > 0) {
timerId = setTimeout(doTick, 10);
}
};
clearTimeout(timerId);
doTick();
};
}());
nav.addEventListener('click', function(event) {
var linkID,
scrollTarget;
if (event.target.tagName.toUpperCase() === "A") {
linkID = event.target.dataset.goto.slice(1);
scrollTarget = main.querySelector('[data-id="' + linkID + '"]');
scrollElementTo(main, scrollTarget, 500);
}
});
Another problem you might have with the event delegation is that if the a elements contain child elements and a child element is clicked on, it will be the target of the event instead of the a tag itself. You can work around that with something like the getParentAnchor function I wrote here.
I hope I understand the problem correctly now: You have markup that you can't change (as it's generated by some means you have no control over) and want to use JS to add functionality to the generated menu items.
My suggestion would be to add id and href attributes to the targets and menu items respectively, like so:
var links = document.querySelectorAll('a'),
paras = document.querySelectorAll('p');
for (var i = 0; i < links.length; i++) {
links[i].href=links[i].getAttribute('data-goto');
}
for (var i = 0; i < paras.length; i++) {
paras[i].id=paras[i].getAttribute('data-id');
}
I'm trying to update a set of divs class="oct_days" to give them id based on :nth-child(n). The format of the id is oct_n. I'm trying to accomplish this using a for loop to set this for divs.
window.onload = function addOctDate() {
var cls = document.getElementByClass("oct_days");
for (var n = 1; n < 32; n++) {
cls[n].id = "oct_" + n;
}
};
Fiddle (http://jsfiddle.net/ascottz/D9Exm/)
The idea is to have .oct_days:nth-child(1) have id="oct_1", but id isn't being set.
clsyour issues are this:
window.onload was being run before your html was initialized
you need to call document.getElementsByClassName not
you are starting your iteration at 1, indexes are 0 based and you should start there and add the + 1 as noted below
also, while iterating, its good to only iterate only over the known items in your list
try this code:
function addOctDate() {
var cls = document.getElementsByClassName("oct_days");
for (n=0, length = cls.length; n < length; n++) {
cls[n].id= "oct_" + (n + 1);
}
};
addOctDate()
The function is getElementsByClassName.
The fiddle doesn't work because you're seeing window.onload while your code is already being run inside that event (the dropdown on the left says onLoad). It'll also error out because you don't have 31 elements in the HTML, but it'll still set the IDs.
Your code is very simple to fix
(function () {
// .getElementsByClassName not .getElementByClass
var cls = document.getElementByClassName("oct_days"),
// set the stopping point DYNAMICALLY
len = cls.length,
// start the index at 0;
n = 0;
for (; n < len; n++) {
cls[n].id = "oct_" + (n + 1);
}
// ()(); auto runs the function
})();
Here is a way to add ids to elements and classes using just plain js.
HTML
<div id="test">
Content will append below!
<input type="button" value="click me!" onClick="myFunction();"/>
</div>
CSS
.cool_0 {
background: red;
height: 200px;
width: 100%;
}
.cool_1 {
background: yellow;
height: 200px;
width: 100%;
}
.cool_2 {
background: red;
height: 200px;
width: 100%;
}
.cool_3 {
background: yellow;
height: 200px;
width: 100%;
}
.cool_4 {
background: red;
height: 200px;
width: 100%;
}
.cool_5 {
background: yellow;
height: 200px;
width: 100%;
}
JS
function myFunction(){
var myId = 0;
var counter = 0;
var myDiv = document.getElementById("test")
for(var i = 0; i < 5; i++){
var textNode = document.createTextNode("sup! My current id is "+myId+" !")
var t = document.createElement("div");
t.setAttribute("id", counter++)
t.setAttribute("class", "cool_"+myId++)
t.appendChild(textNode)
myDiv.appendChild(t);
}
}