Learning JavaScript and this is my first ever real 'project''
I've created a loop to check that the .background element is smaller than the client, if it is then add the the slice node until the .background element is the same height as the client.
The issue is that too many slice nodes are being added and sometimes crashing the browser.
Also, is there a way to clone the HTML node and repeat it instead of using:
background.innerHTML += '<span class=slice></span>';
If is there a better way of doing what I'm trying to do then please do let me know!
JSFiddle
http://jsfiddle.net/8ryAD/11/
HTML
<div class="master">
<a id="btn-nav">
<span></span>
<span>Menu</span>
<span></span>
</a>
<nav id="main-nav">
<div class="background">
<div class="slice"></div>
</div>
</nav>
</div><!-- end master -->
JS
(function () {
'use strict';
var w = document.documentElement.clientHeight;
var menuBtn = document.getElementById('btn-nav');
var slice = document.querySelectorAll('.slice');
var background = document.getElementsByClassName('background')[0];
//var mainNavHeight = document.getElementById('main-nav').clientHeight;
//var slideAdd = slice.cloneNode(true);
//var windowHeight = window.innerHeight;
menuBtn.addEventListener('click', function() {
menuBtn.classList.add('open');
for(var i = background.clientHeight; i < w; i++) {
background.innerHTML += '<span class=slice></span>';
//for(var i = 0; i < slice.length; i++){
// slice[i].style.opacity = 1;
// }
}
}, false);
}());
Done it. If someone knows a better way of doing this then please do let me know! Thanks.
var slice = document.querySelectorAll('.slice');
var menuBtn = document.getElementById('btn-nav');
var b = document.getElementsByClassName('background')[0];
var a = document.getElementById('main-nav').clientHeight
menuBtn.addEventListener('click', function() {
menuBtn.classList.add('open');
for(var i = b.clientHeight; i < a; i++) {
if(b.clientHeight == a) {
break;
} else {
b.innerHTML += '<span class=slice></span>';
}
}
}, false);
Related
I was actually using a script which allowed me to Show a div onclick and hide others but now I need to do the same with "class" instead of "id".
My current script:
function layout(divName){
var hiddenVal = document.getElementById("tempDivName");
if(hiddenVal.Value != undefined){
var oldDiv = document.getElementById(hiddenVal.Value);
oldDiv.style.display = 'none';
}
var tempDiv = document.getElementById(divName);
tempDiv.style.display = 'block';
hiddenVal.Value = document.getElementById(divName).getAttribute("class");}
What I tried using getElementsByClassName :
function layoutNEW(divName){
var hiddenVal = document.getElementById("tempDivName");
if(hiddenVal.Value != undefined){
var oldDiv = document.getElementById(hiddenVal.Value);
oldDiv.style.display = 'none';
}
var tempDiv = document.getElementsByClassName(divName);
for ( var i=0, len=tempDiv.length; i<len; ++i ){
tempDiv[i].style.display = 'block';
}
hiddenVal.Value = document.getElementById(divName).getAttribute("id");}
Any ideas ?
EDIT : A working example of my current script with "id" : JSFiddle
EDIT 2: It works great, but when the div (class) is cloned, only one of them is showing the div. Do you have an idea about this ? Where is a JSFiddle demonstrating the situation: JSFiddle
I think this is what you'd need. The idea is that you can use a data property on your <a> tags that will tell your click handler which classname to look for when showing an element. From there, you just hide the others. Here's a working demo:
var toggleControls = document.querySelectorAll("[data-trigger]");
var contentDivs = document.querySelectorAll(".toggle");
for (var i = 0; i < toggleControls.length; i++) {
toggleControls[i].addEventListener("click", function(event) {
var trigger = event.target;
var selector = "." + trigger.getAttribute("data-trigger");
var divToShow = document.querySelector(selector);
for (j = 0; j < contentDivs.length; j++) {
contentDivs[j].style.display = "none";
}
divToShow.style.display = "block";
});
}
.toggle {
height: 100px;
width: 100px;
display: none;
}
.div1 {
background-color: red;
}
.div2 {
background-color: blue;
}
.div3 {
background-color: purple;
}
.div4 {
background-color: green;
}
Show Div1
<br/>
Show Div2
<br/>
Show Div3
<br/>
Show Div4
<div class="toggle-container">
<div class="toggle div1"></div>
<div class="toggle div2"></div>
<div class="toggle div3"></div>
<div class="toggle div4"></div>
</div>
EDIT - As per updated question
In order to get this to work with dynamically created elements, you will have to put the var contentDivs = ... inside of the click handler, so you get a live version of that array. Also, you will need to change .querySelector to .querySelectorAll as the former only grabs the first matching element, not all as the latter does.
Here's what the code would look like: (note - I also moved the click handler into an outside function so it was not being recreated for every iteration of the loop, as is good practice)
function clickHandler(event) {
var contentDivs = document.getElementsByClassName("toggle"); // get live set of contentDivs in case any were added dynamically
var trigger = event.target;
var selector = "." + trigger.getAttribute("data-trigger");
var divsToShow = document.querySelectorAll(selector); // grab all matching divs
for (var i = 0; i < contentDivs.length; i++) {
contentDivs[i].style.display = "none";
}
for (var j = 0; j < divsToShow.length; j++) {
divsToShow[j].style.display = "block";
}
}
var toggleControls = document.querySelectorAll("[data-trigger]");
for (var i = 0; i < toggleControls.length; i++) {
toggleControls[i].addEventListener("click", clickHandler);
}
function cloneDiv() {
var elmnt = document.getElementsByClassName("container");
for ( var i=0; i<elmnt.length; i++ ) {
var cln = elmnt[i].cloneNode(true);
}
document.body.appendChild(cln);
document.getElementById("clone").appendChild(cln);
}
window.onload = cloneDiv();
I am trying to write a constructor function with a method that loops over a number of rows (numerical argument), and for each of the individual rows then loops over a number of dots(numerical argument). In Each of the two loops a portion of html will be rendered. I can get the first portion html to render but when i try to loop over second portion using a selector from the previously rendered html nothing happens. What am i missing ?
HTML CODE
<div class="container">
<h1 class="heading">Random Colors</h1>
<div class="row">
<div id="frame" class="col-xs-12">
<!-- CONTENT PUSHED BY JAVASCRIPT -->
</div>
</div><!-- Ends .row -->
</div><!-- Ends .container -->
JAVASCRIPT CODE
var ColorDots = function(rows, dots) {
this.numOfRows = rows;
this.numOfDots = dots;
this.renderDots();
};
ColorDots.prototype.renderDots = function() {
this.rowTemplate = '<div class="color-dot row"></div>';
this.iconTemplate = '<i class="fa fa-circle icon" aria-hidden="true"></i>';
for ( var r = 0; r < this.numOfRows; r++) {
document.getElementById('frame').innerHTML += this.rowTemplate;
for ( var i = 0; i < this.numOfDots; i++) {
document.getElementsByClassName('color-dot').innerHTML += this.iconTemplate;
}
}
};
SOLUTION (javascript only)
var ColorDots = function(rows, dots) {
// Properties
this.numOfDots = dots;
this.numOfRows = rows || 1;
this.iconHtml = '<i class="fa fa-circle icon" aria-hidden="true"></i>';
this.renderRows();
};
ColorDots.prototype.renderRows = function() {
this.rowHtml = '';
for ( var r = 0; r < this.numOfRows; r++) {
this.rowHtml += '<div class="color-dot row">';
for ( var i = 0; i < this.numOfDots; i++) {
this.rowHtml += this.iconHtml;
}
this.rowHtml += '</div>';
}
document.getElementById('frame').innerHTML = this.rowHtml;
};
Thank you #rainerh for giving me the answer to my question. How ever after considering what #shilly said in the comment in regards to using .innerHTML within for loops i made some changes to my code to reflect his suggestion. Hopefully this will be useful for other trying to do something similar to me.
First the css class is color-dot and not color-dots as you have it in your selector.
Secondly getElementsByCLassName returns an array. So have to use an index here.
ColorDots.prototype.renderDots = function() {
this.rowTemplate = '<div class="color-dot row"></div>';
this.iconTemplate = '<i class="fa fa-circle icon" aria-hidden="true"></i>';
for ( var r = 0; r < this.numOfRows; r++) {
document.getElementById('frame').innerHTML += this.rowTemplate;
for ( var i = 0; i < this.numOfDots; i++) {
document.getElementsByClassName('color-dots')[r].innerHTML += this.iconTemplate;
}
}
};
I'm trying to make a directive that I can do a virtual scroll, so as the user scrolls the table, the table remove "old" views and add "new" views, kind like of collection repeat but I've been failing, I think I didn't understand the math behind it, can someone help me?
this is my directive code:
BaseModule.directive('myScroll', function() {
return {
restrict:"A",
scope:{
rows:"=",
headers:"="
},
link: function(scope,el) {
var scrollTop = 0;
var scrollLeft = 0;
angular.element(el).on('scroll',function(){
scrollTop = $(el).scrollTop();
scrollLeft = $(el).scrollLeft();
$(el).parent().find(".header").scrollLeft(scrollLeft);
var height = $(el).height();
var numberOfRows = height/23;
var initialRow = scrollTop/23;
var html = "";
for(i=0; i<numberOfRows;i++){
var row = scope.rows[i+initialRow];
html = html + addRow(row,i+initialRow);
}
$(el).find('.tbody-scroll').append(html);
});
scope.$watch('rows',function(rows){
var height = $(el).height();
var numberOfRows = height/23;
var initialRow = scrollTop/23;
var html = "";
for(i=0; i<numberOfRows;i++){
var row = rows[i+initialRow];
html = html + addRow(row,i+initialRow);
}
$(el).find('.tbody-scroll').append(html);
});
var addRow = function(row,index){
var html = "";
var pos = 0;
var totalWidth = 0;
angular.forEach(row,function(col){
var width = scope.headers[pos].width;
totalWidth = totalWidth + width;
html = html + "<span style='width:"+width+"px'>"+col.value+"</span>";
pos++;
});
html = "<div class='row' style='top:"+index*23+"px;width:"+totalWidth+"px;'>"+html;
html = html + "</div>";
return html;
};
}
};
});
<!-- my directive .html -->
<div class="mTable">
<div class="header" ng-style="headerWidth(headers())">
<span ng-repeat="header in headers()" ng-style="widthStyle(header)">
{{::header.label}}
</span>
</div>
<div class="tbody-container" my-scroll headers="headers()" rows="rows()">
<div class="tbody-scroll" ng-style="scrollHeight(rows(),headers())"></div>
</div>
</div>
Giving a full answer with code might require a bit too much of an effort
This library implements virtual scroll on ng-repeat https://github.com/stackfull/angular-virtual-scroll in the description there's also an article on how to implement this feature on your own.
The basic concept is to make two divs, one above and one below the list, which size is determined by the number of elements inside the list (this approach has a limitation since list elements must either have all the same height or their height must be fixed), then you delete elements as they disappear from the viewport and resize the divs according to the number of elements not currently rendered and your position on the list
Not a direct answer to your question, but an alternative: You may want to look at the ui-scroll directive which is a replacement for ng-repeat and has a similar function.
Example
in your controller
$scope.movieDataSource = {
get : function (index, count, callback) {
var i, items = [$scope.userMovies], item;
var min = 1;
var max = 1000;
for (i=index ; i<index + count ; i++) {
if(i < min || i > max) {
continue;
}
item = {
title: $scope.userMovies.title,
imageURL: $scope.userMovies.poster_path
};
items.push (item);
}
callback (items);
}
}
And in your view:
<div ui-scroll="item in movieDataSource">
{{item.title}}
</div>
Please Look at the following code only the last image moves.
http://jsfiddle.net/u8Bg3/
But second one works
http://jsfiddle.net/u8Bg3/1/
As pointed by the Er144 even this works with jquery
http://jsfiddle.net/u8Bg3/14/
I also found out appendchild works but not innerhtml
The difference between two is that in first one html exits in second one it's dynamically created
HTML
<body>
<div class="racetrack" id="racetrack"></div>
<div id="track-tmpl" class="hide">
<div class="track"><div id="player{{ x }}" class="runner"></div></div>
</div>
</body>
JS
var position = [0,40,80,120,80],
racetrack = document.getElementById('racetrack');
track_tmpl = document.getElementById('track-tmpl').innerHTML;
function Players(ele, ptimeout)
{
this.el = ele;
this.i = 0;
this.iterations = 0;
this.stop = 0;
this.timeout = ptimeout;
this.position = 0;
this.animate = function(){
if(this.i !== 0){
this.move((this.position + 5), this.i);
}
if(!this.stop){
if(this.i < 5){
setTimeout(function(_this){
_this.i++;
_this.animate();
},this.timeout,this);
}
if(this.i==5){
this.iterations ++;
if(this.iterations < 50){
this.i = 0;
this.animate();
}
else{
this.el.style.backgroundPosition = '120px 0px';
}
}
}
};
this.start = function(){
this.stop = 0;
this.animate();
};
this.move = function(to,positionIndex){
this.position = to;
this.el.style.backgroundPosition = '-'+position[positionIndex]+'px 0px';
this.el.style.webkitTransform = 'translate('+to+'px)';
this.el.style.mozTransform = 'translate('+to+'px)';
}
}
function Game(noOfPlayers){
this.noOfPlayers = noOfPlayers;
this.players = new Array();
for (var i = 0; i < this.noOfPlayers ; i++){
racetrack.innerHTML = racetrack.innerHTML + track_tmpl.replace('{{ x }}', i);
this.players.push(new Players(document.getElementById('player' + i), (120 + i)));
/* issue here with dynamic added content*/
}
this.start = function(){
for (var i = 0; i < this.noOfPlayers; i++){
this.players[i].start();
}
};
}
var game = new Game(3);
game.start();
Why is that in dynamically added html only the last one moves
The issue is with creating the player(n) object inside the for loop along with the assignments to innerHTML using `+='. The modified fiddle: http://jsfiddle.net/u8Bg3/15/ works fine. Cheers for a good question!
var finalized_tracks= "" ;
for (var i = 0; i < this.noOfPlayers ; i++){
finalized_tracks += track_tmpl.replace('{{ x }}', i);
}
racetrack.innerHTML = racetrack.innerHTML + finalized_tracks;
for (var i = 0; i < this.noOfPlayers ; i++){
this.players.push(new Players(document.getElementById('player'+ i),(120+i)));
}
If you use the jquery:
var element = track_tmpl.replace('{{ x }}', i);
$(racetrack).append(element);
instead of the line where you change the innerHtml of racetrack div, all elements are moving.
However, I'm not sure, why...
theCoder has pretty much nailed the issue with your code there.
Just as an additional thought, you could manually build the necessary divs using javascript instead, it's more long winded however...
for (var i = 0; i < this.noOfPlayers ; i++){
var newTrack = document.createElement("div");
newTrack.id = "track"+i;
newTrack.className = "track";
var newPlayer = document.createElement("div");
newPlayer.id = "player"+i;
newPlayer.className = "runner";
newTrack.appendChild(newPlayer);
racetrack.appendChild(newTrack);
this.players.push(new Players(document.getElementById('player' + i), (120 + i)));
}
I've not had much luck trying to incorporate this idea/solution/feature into a link such as:
HOME
The text HOME would be able to switch between various other words, in an ideal world..
Any advice would be appreciated.
Thanks!
This is a possible solution
<a id="link" href="#" title="home">HOME</a>
var array = [];
array.push("home");
array.push("away");
array.push("there");
array.push("there");
array.push("back");
array.push("moon");
array.push("stars");
var link = document.getElementById("link");
var index = 0;
var length = array.length;
var period = 1000; // miliseconds
var id;
function changeLink() {
index += 1;
if (index >= length) {
index = 0;
}
link.textContent = array[index].toUpperCase();
link.title = array[index];
}
id = setInterval(changeLink, period);
on jsfiddle