Javascript not entering a FOR loop inside a function - javascript

I came here a few days ago asking for help on creating a grid of squares in JS/CSS.
A very helpful person came here and guided me on using two nested fors and createElement('div'); to get the job done. However, his example was a code that went directly into doing that: http://jsfiddle.net/3x1kmcme/
I need the action to actually happen when the user clicks a button, using a .click() JQuery function. This is not working, and no error is being shown. I really did try going through the code itself, and even changed it, declared it beforehand as a variable, and went line by line to check where the error lies, it seems it's not entering the FOR loop, I could be wrong, of course.
Perhaps something obvious I'm missing?
var rows = 8,
cells = 8,
count = 0;
var i, j,
top = 0,
left = 0;
var boxWidth = 50,
boxHeight = 50;
var $canvas = $('#canvas');
var $fragment = $(document.createDocumentFragment());
$(document).ready(function () {
"use strict";
$("#btnstart").click(function () {
function addBox(opts) {
var div = document.createElement('div');
div.id = opts.id;
div.className = 'alive';
div.style.top = opts.top + "px";
div.style.left = opts.left + "px";
$fragment.append(div);
}
for (j = 0; j < rows; j += 1) {
top = j * boxHeight;
for (i = 0; i < cells; i += 1) {
count += 1;
addBox({
count: count,
id: 'item' + i,
top: top,
left: i * boxWidth
});
}
}
$canvas.html($fragment);
});
});

It seems to work just fine at the following fiddle
Not really sure what exactly the problem is. I added the following changes to the HTML
<div id="canvas"></div>
<input type='button' id='btnstart' value='Start' />

I figured out the problem AND the reason why it worked on JSFiddle. Even if I wrapped the JavaScript file in a Document.Ready function, I still had to put the scripts at the bottom of the page, just below the tag.

Related

Vanilla Javascript: Infinite Image Marquee

Okay so I must create an infinite auto-scrolling horizontal image marquee using vanilla JS. I have the following code:
//if(painkiller<14){painkiller++;} else{painkiller=0;backup2()}
var speed = 5;
var exeggcute = true;
var painkiller = 0;
var marquix = document.getElementById("marquis");
var backup = "";
var coquus = 0;
for (var painkiller = 0; painkiller < 15; painkiller++) {
backup += "<img class='slide' src='" + ImgArray[painkiller].src + "' width='" + ImgArray[painkiller].width + "'>";
}
marquix.innerHTML = backup;
function riverflow() {
marquix.scrollLeft += 5;
if (marquix.children[0].getBoundingClientRect().left <= (marquix.children[0].width * -1)) {
marquix.appendChild(marquix.children[0]);
//marquix.getBoundingClientRect().left=0;
//marquix.children[0].style.transform="translateX(133px)"
}
}
//function backup2(){marquix.innerHTML=backup;}
setInterval('riverflow()', 50);
exeggcute = true;
<head>
<script>
var ImgArray = [];
for (var i = 0; i < 15; i++) {
ImgArray[i] = new Image();
ImgArray[i].src = "imgx/imagen" + (i + 1) + ".jpg";
ImgArray[i].width = 133;
}
</script>
</head>
<body>
<div id="marquis">
</div>
</body>
Basically I'm creating the image chain, then filling with it the innerHTML of a div, then assigning said div to a variable, and finally calling a repetitive function through setInterval(). Now, what that function does is a simple scroll to the left and - when the first image is completely out of the viewport - use appendChild to rip the first child element or img from the image chain then put it at the end of it. So no image overcharge is produced and the marquee uses the same 15 element once and again.
Here's my problem, though: when the appendChild function fires, the image that's out of the viewport is removed, however, the next image in line - as well as the rest of the chain - does not preserve its current position, and is instead forcefully scrolled to fill the gap left by the then-first image that's now at the end. Thus, the condition of the appendChild (which was the first children of the div being completely out of the viewport) becomes true and activates the function - leading the whole marquee to slide non-stop and out of control, as the appendChild is firing continuously.
How can I fix it?
Possible solutions:
You will need to reset the scrollLeft to 0 on the same moment that you switch the images.
You will need to add some element (another img for example at the begining) it could be all white or transparent. That image will be always there, before the firstone visible. When you remove the other image this auxiliar image need to be wider (change the width) to fill the gap, so add to its width the width of the removed image each time you remove one.
Or you can change the marginLeft of the leftmost image with marquix.children[0].style.marginLeft = n + "px";
That's what I came up with, but as I said, the translateX() parameter increases ad infinitum.
<script>
var ImgArray=[];
for (var i=0; i<15; i++){
ImgArray[i]= new Image();
ImgArray[i].src= "imgx/imagen"+(i+1)+".jpg";
ImgArray[i].width=133;
}
</script>
</head>
<body>
<div id="marquis">
<script>
//if(painkiller<14){painkiller++;} else{painkiller=0;backup2()}
var exeggcute= true;
var painkiller=0;
var marquix = document.getElementById("marquis");
var backup=""; var coquus=0;
for(var painkiller =0;painkiller<15;painkiller++){
backup+="<img class='slide' src='"+ImgArray[painkiller].src+"' width='"+ImgArray[painkiller].width+"'>";
}
marquix.innerHTML=backup;
var slidin=133;
function riverflow(){
marquix.scrollLeft+=10;
if (marquix.children[0].getBoundingClientRect().left<=(marquix.children[0].width*-1) && exeggcute){
marquix.appendChild(marquix.children[0]);
marquix.getBoundingClientRect().left=0;
for (var j=0; j<15; j++){
marquix.children[j].setAttribute("style","transform: translateX("+slidin+"px)")
}
slidin+=133;
}
}
//function backup2(){marquix.innerHTML=backup;}
setInterval('riverflow()',50);
exeggcute=true;
</script>

Slide dynamically added content with jQuery

I'm trying to create a simple content slider that could handle dynamically added content to the slider. None of the "lightweight" plugins I found provided such functionality or, if it did, it didn't work correctly.
var $left = $('.left'),
$right = $('.right'),
$months = $('.sub ul');
$left.click(function(){
for(i = 0; i < 3; i++){
$months.find('li').first().before($.parseHTML('<li>xxx</li>'));
}
pos = $months.position();
$months.css('left', pos.left + 90);
});
$right.click(function(){
for(i = 0; i < 3; i++){
$months.find('li').last().after($.parseHTML('<li>xxx</li>'));
}
pos = $months.position();
$months.css('left', pos.left - 90);
});
This is the code I've got so far and here's a fiddle with an example - http://jsfiddle.net/kkr4zg0r/2/. It kind of works, but the problem is that since new content is added the navigation goes off (you can see what I mean by clicking left-right a couple of times).
I understand what's the problem for this - the newly added items "shift" the content and I need to do better calculations than substracting/adding 90px to the left position of the element but I can't figure out how to get the correct index of the elements and basically get this sliding by exactly (and correctly) by 3(or 6) elements at the time.
Currently the code is adding extra elements whenever a navigation button is pressed, if I could get the index of the currently visible first/last element, I could probably tell whether I need to add more elements and only add them then.
This is a basic illustration of what I'm trying to achieve
edit
I've changed the jsfiddle to the correct one.
The whole idea is to check when adding elements is necessary and when shift is enough:
Fiddle
$(document).ready(function()
{
var $main = $('.main'),
$left = $('.left'),
$right = $('.right'),
$months = $('.sub ul');
var addCount = 3;
var liWidth = 30;
var shiftX = addCount * liWidth;
$left.click(function()
{
var currentLeft = parseInt($months.css('left'));
var totalLength = $months.find('li').length * liWidth;
if (-currentLeft + $main.width() >= totalLength)
{
for (var i = 0; i < addCount; i++)
{
$months.find('li:last').after('<li>xxx</li>');
}
}
$months.css('left', currentLeft -= shiftX);
});
$right.click(function()
{
var currentLeft = parseInt($months.css('left'));
if (currentLeft < 0)
{
$months.css('left', currentLeft += shiftX);
}
else
{
for (var i = 0; i < addCount; i++)
{
$months.find('li:first').before('<li>xxx</li>');
}
}
});
});

scope of ng-model in angular

Here's the html part...
<body ng-controller="homeController">
<div id="happyValentines" ng-model="myText"> {{ myText }}
I don't get why this works...
$scope.myText = "fksdm kdsmfk msldfkm kdfm ksdmf lsmdflm ";
$scope.changeHeight = function(elem) {
var body = document.body;
var height = 0;
var right = 0;
var opacity = 0; ...
But this doesn't...
$scope.changeHeight = function(elem) {
var body = document.body;
var height = 0;
var right = 0;
var opacity = 0;
body.style.opacity = 0;
var index = 0;
var str = "fksdm kdsmfk msldfkm kdfmksdmf lsmdflm ";
function frame() {
index += 2;
$scope.myText = str.substr(0, index);
height += 4;
if (right < 60) {
right += 1;
elem.style.marginRight = right + "px";
}
opacity += .01;
elem.style.height = height + "px";
elem.style.opacity = opacity;
body.style.opacity = opacity;
if (opacity >= 1) {
clearInterval(id);
$scope.addButtons();
// document.createElement....
}
}
var id = setInterval(frame, 30);
}
I'm quite new to angular and would like to know what angular construct is behind this, or why this is the case.
In the 2nd code you have declared function 'changeHeight', and inside that you are setting the value of 'myText'. but on the page you have not called 'changeHeight' function so it'll not call that function and the value of 'mytext' won't be set.
try calling function 'changeHeight' on the page using ng-init or ng-click or ng-change a/c to situation.
First of all: ng-model is mainly used for form elements and is basically a deluxe version of using onchange events for get value from forms to the model and a $watch to get the value from the model to the form element. You don't need it just to display a value on the screen.
Second: You shouldn't modify the document or DOM in the controller. That's the directives responsibility (or maybe a service if it's document element modifications).
Regarding your second code block I don't get why you have a "function frame()" inside the changeHeight method. And the code is incomplete and you never actually call the frame() method that sets the $scope.myText.
Please provide complete code to get better help.

Pre-formatting text to prevent reflowing

I've written a fairly simple script that will take elements (in this case, <p> elements are the main concern) and type their contents out like a typewriter, one by one.
The problem is that as it types, when it reaches the edge of the container mid-word, it reflows the text and jumps to the next line (like word wrap in any text editor).
This is, of course, expected behavior; however, I would like to pre-format the text so that this does not happen.
I figure that inserting <br> before the word that will wrap would be the best solution, but I'm not quite sure what the best way to go about doing that is that supports all font sizes and container widths, while also keeping any HTML tags intact.
I figure something involving a hidden <span> element, adding text to it gradually and checking its width against the container width might be on the right track, but I'm not quite sure how to actually put this together. Any help or suggestions on better methods would be appreciated.
Edit:
I've managed to write something that sort of works using jQuery, although it's very sloppy, and more importantly, sometimes it seems to skip words, and I can't figure out why. #content is the name of the container, and #ruler is the name of the hidden <span>. I'm sure there's a much better way to do this.
function formatText(html) {
var textArray = html.split(" ");
var assembledLine = "";
var finalArray = new Array();
var lastI = 0;
var firstLine = true;
for(i = 0; i <= textArray.length; i++) {
assembledLine = assembledLine + " " + textArray[i];
$('#ruler').html(assembledLine);
var lineWidth = $('#ruler').width();
if ((lineWidth >= $('#content').width()) || (i == textArray.length)) {
if (firstLine) { var tempArray = textArray.slice(lastI, i); }
else { var tempArray = textArray.slice(lastI+1, i); }
var finalLine = tempArray.join(" ");
finalArray.push(finalLine);
assembledLine = "";
if (lineWidth > $('#content').width()) { i = i-1; }
lastI = i;
firstLine = false;
}
}
return finalArray.join("<br>");
}
You could use the pre tag: Which displays pre-formatted text, or you could put the content into a div tag, set a fixed width, and script based upon that.
The best way (IMO) would be to add the whole word, but have the un-"typed" letters invisible. E.g:
H<span style="visibility: hidden;">ello</span>
He<span style="visibility: hidden;">llo</span>
Hel<span style="visibility: hidden;">lo</span>
Hell<span style="visibility: hidden;">o</span>
Hello
To make it easier, give the span a name, and delete it from the DOM each time.
A possible approach is to set p display inline (because default display-block will make p to consume all width even if it has just 1 character) and then as you 'type' check the element width.
Set a tolerance in px (25px for example) and once p's width reaches total available width minus width tolerance you insert <br />
I think this should work...
After playing with the code I edited into the question, I managed to get it working decently.
Code:
function formatText(html) {
var textArray = html.split(" ");
var assembledLine = "";
var finalArray = new Array();
var lastI = 0;
var firstLine = true;
for(i = 0; i <= textArray.length; i++) {
assembledLine = assembledLine + " " + textArray[i];
$('#ruler').html(assembledLine);
var lineWidth = $('#ruler').width();
if ((lineWidth >= $('#content').width()) || (i == textArray.length)) {
if (firstLine) { var tempArray = textArray.slice(lastI, i); }
else { var tempArray = textArray.slice(lastI+1, i); }
var finalLine = tempArray.join(" ");
finalArray.push(finalLine);
assembledLine = "";
if (lineWidth >= $('#content').width()) { i = i-1; }
lastI = i;
firstLine = false;
}
}
return finalArray.join("<br>");
}
Not perfect, but it'll do. Thanks, everyone.

Javascript Only Works with alert, why?

The statement I'm concerned about in the following statement is fancybox = 1; That needs to happen if my maxWidth for any of the temporary images I create is over 480.
A little background on the html this interacts with:
I have a link wrapped around an image.
The image is a resized version, and the link's href is to the original, unsized image.
There is a series of these link wrapped images
A div, addon-large-image, wraps the whole thing.
For some reason, this code works if I have 'alert(m);' included. I correctly end up in side my final if statement (in this case I do have images wider then 480) and the last alert I get is "Triggered". However, if I comment out 'alert(m);', and change nothing else, 'alert("Triggered");' fails to fire, showing me that I have not, in fact, entered my last conditional.
Any thoughts on what I'm doing wrong here? I have a programming background in Java, but I'm relatively new to Jquery in any heavy sense, so I'm guessing I have a syntax problem of some sort that 'alert(m);' is sort of incidentally fixing.
'tallest' is irrelevant in the scope of my problem, it does what it's supposed to correctly, is used elsewhere, and existed before I implemented maxWidth.
var tallest = 0;
var tempImg = new Image();
var tempSrc = "";
var maxWidth = 0;
// Finds the tallest image in the set.
$("#addon-large-image img").each(function () {
var n = $(this).attr("height");
if (tallest < n) {
tallest = n;
}
tempSrc = $(this).parent().attr("href");
$(tempImg).attr("src", tempSrc);
var m = $(tempImg).attr("width");
alert(m);
if (maxWidth < m) {
maxWidth = m;
}
});
if (maxWidth > 480) {
fancybox = 1;
alert("Triggered");
}
Looks like something isn't fully loaded in your script yet. Try running this in a jQuery document.ready and see if it works.
Did you use Ajax Request ?
if YES then put you code that didnt work at the end of
AjaxObject.onreadystatechange = function () {
if (AjaxObject.readyState == 4) {
.
.
.
.
.
// PUT HERE
}
}
then if No
use this :
function testName()
{
$(document).ready(function(){
var tallest = 0;
var tempImg = new Image();
var tempSrc = "";
var maxWidth = 0;
// Finds the tallest image in the set.
$("#addon-large-image img").each(function () {
var n = $(this).attr("height");
if (tallest < n) {
tallest = n;
}
tempSrc = $(this).parent().attr("href");
$(tempImg).attr("src", tempSrc);
var m = $(tempImg).attr("width");
alert(m);
if (maxWidth < m) {
maxWidth = m;
}
});
});
return true;
}
hope helps you buddy

Categories