my div slider ignores the borders that i've made, can't find the mistakes.
P.S. right/left functions are called in tag attribute onclick:'slider.right()'. I'm just learning, I know that the elegance of the code is far from being ideal.
let elArr = [];
let pusher = elArr.push(document.querySelectorAll('#scr>div'));
let elements = Array.from(elArr[0]);
let slider = {
frame: 0,
set: function(element){
var container = document.getElementById('scr');
container = element.style.visibility='visible';
},
init: function(){
this.set(elements[this.frame]);
},
left: function(){
elements[this.frame].style.visibility='hidden';
this.frame--;
if(this.frame<0)
this.frame = elements.length - 1;
this.set(elements[this.frame]);
},
right: function(){
elements[this.frame].style.visibility='hidden';
this.frame++;
if(this.frame>elements.length)
this.frame = 0;
this.set(elements[this.frame]);
}
};
window.onload = function(){
slider.init();
}
One great guy just helped me, so there is an incorrect condition
if(this.frame>elements.length)
it drops 'frame' value, when current value is bigger then the number of elements in the array, but taking in consideration the fact, that indexation of the elements starts with 0, when frame is equal to elements.length - value becomes undefined, so frame value doesn't drop.
So, we need to change condition to a comparison == or to make it >=;
thnx to user:186999 for the decision
Related
I'm trying to add and remove paragraph elements that are 1em (16px) wide both initially and when the window is resized. When the script first loads, it adds too many paragraph elements about 2 - 6 in most cases, and when I resize the window it either adds too many or removes to many I'm not sure what is causing it to go over or under the difference. I'm trying to accomplish this with vanilla javascript.
Edit: The paragraphs are meant to be vertical, and a single character wide 16px. I will then have characters randomly and continuously generate and fall down the screen.
(function(window, undefined){
var parentContainer = document.getElementsByClassName('stringFall_Container'),
paras = document.getElementsByClassName('para'),
containerWidth,
paraWidth,
difference,
paraAmount;
function checkContainerWidth () {
console.log('Running checkContainerWidth();')
containerWidth = parentContainer[0].offsetWidth;
console.log('The containers size is:' + containerWidth)
return true;
}
function checkParaWidth () {
console.log('Running checkParaWidth();')
paraWidth = paras[0].offsetWidth;
console.log('The Paragraphs size is:' + paraWidth)
return true;
}
function checkParaAmount () {
console.log('Running checkParaAmount();');
paraAmount = paras.length;
console.log(paraAmount);
return true;
}
function checkDifference () {
console.log('Running checkDifference();');
difference = containerWidth / paraWidth;
return true;
}
function addPara (number) {
console.log('Running addPara();');
number = number === undefined ? 1 : number;
console.log(number);
for (var i = 0; i < number; i++) {
var create = document.createElement('p');
parentContainer[0].appendChild(create).setAttribute('class', 'para');
}
return true;
}
function removePara (number) {
console.log('Running removePara()');
var lastElement = paras[paras.length - 1];
checkParaAmount();
number = number === undefined ? 1 : number;
for (var i = 0; i < number; i++) {
parentContainer[0].removeChild(lastElement);
}
return true;
}
function executeOnResize () {
checkDifference();
console.log('Running executeOnResize();');
checkContainerWidth();
if (difference < paraAmount) {
addPara(paraAmount - difference);
} else {
removePara(difference - paraAmount)
}
}
addPara();
checkContainerWidth();
checkParaWidth();
checkParaAmount();
checkDifference();
console.log(difference);
addPara(difference);
window.addEventListener('resize', executeOnResize, false);
})(this);
In such scenarios it is highly recommended to post a complete example, because it matters what styles are applied to the paragraphs and their container. An example will also help people understand what you are trying to do faster and more easily.
There are some issues in the algorithm, which standard debugging should reveal:
the initial adding of paragraphs should take into account that you already have one rendered: addPara(difference - 1);
before adding or removing paragraphs in executeOnResize, you should update the paraAmountValue value, as it is still 1;
when calculating the difference, you may want to disregard the decimal part and obtain an integer value, otherwise you will add or remove more paragraphs than necessary
the conditional statement in executeOnResize should allow a case when you neither have to add paragraphs, nor remove any
are you sure you want to add paragraphs when difference is less than paraAmount? Shouldn't it be the other way around?
Here is my test page, please review it. I hope it will help you go on.
http://dojo.telerik.com/AVoKU
You will notice that at some points, there is one paragraph falling on the second line - this is something that still needs to be fixed.
I'm trying to make a drag and drop, but it's been giving me a bunch of issues. I fix one and another comes up. I had it working where it would see if any part of my drag object entered a target area, but I wanted it to just recognize one part (it's graphic of a pointy thermometer, and you can't measure temperature with the head in real life) demo here.
The error I'm getting is that "Uncaught TypeError: Cannot read property 'drag' of undefined" (I labeled 'drag' on the demo, but its a child movieclip inside the drag object)
also, 'thermometer' and 'drag' are both named instances of movieclips.
The Code:
var dragger = this.thermometer;
//tried this to see if it would help
var tip = this.thermometer.drag;
var right = this.targetRight;
var wrong = this.targetWrong;
For moving it:
dragger.on("pressmove", function(evt){
evt.currentTarget.x = evt.stageX;
evt.currentTarget.y = evt.stageY;
if(intersect(tip, right)){
evt.currentTarget.alpha=0.2;
}
else if(intersect(tip, wrong)){
evt.currentTarget.alpha=0.7;
}
else{
evt.currentTarget.alpha=1;
}
stage.update(evt);
}, this);
For releasing it
dragger.on("pressup", function(evt){
//lock position of drag object and play animation if any
dragger.x = dragger.x;
dragger.y = dragger.y;
if(hitTestArray.length > 1){
dragger.alpha = 1;
stage.update(evt);
}//else{
if(intersect(tip, right)){ //Intersection testing for good (also tried 'dragger.drag' to see if that would work. it didn't)
alert("YAY you're right AND it works!");
dragger.alpha = 1;
}else if(intersect(tip, wrong)){ //intersection Testing for bad
alert("BOO its wrong, but YAY it works");
dragger.alpha = 1;
}
stage.update(evt);
//}
}, this);
UPDATED INTERSECT:
for testing intersection.
function intersect(obj1, obj2){
var objBounds1 = obj1.nominalBounds.clone();
var objBounds2 = obj2.nominalBounds.clone();
var pt = obj1.globalToLocal(objBounds2.x, objBounds2.y);
var h1 = -(objBounds1.height / 2 + objBounds2.height);
var h2 = objBounds2.height / 2;
var w1 = -(objBounds1.width / 2 + objBounds2.width);
var w2 = objBounds2.width / 2;
if(pt.x > w2 || pt.x < w1) return false;
if(pt.y > h2 || pt.y < h1) return false;
return true;
}
To sum up, I need to know how to make it not undefined so that I can test for that little box being in one of the big boxes.
The error is happening because you used this.currentTarget instead of evt.currentTarget on that one line.
Note that your actual code is not the same as the code you posted above. Here is the code in the live demo:
if(intersect(this.currentTarget.drag, right)){
evt.currentTarget.alpha=0.2;
} else if(intersect(this.currentTarget.drag, wrong)){
evt.currentTarget.alpha=0.7;
}
else{
evt.currentTarget.alpha=1;
}
Not sure if this will solve all your issues, but it should at least get you moving forward.
[UPDATE]
Looking a little deeper, there a number of issues that are likely contributing to your intersect function not working:
Your right/left bounds are not correct. EaselJS objects do not have a width or height (more info), so the bounds you set just have an x and y.
You can use nominalBounds to get the proper bounds. This provides the untransformed, original bounds of the symbol. You will have to account for any scaling. In this case, the bounds are:
* left: {x: 0, y: 0, width: 275, height: 300}
* right: {x: 0, y: 0, width: 413, height: 430}
Your intersection will have to consider the display list hierarchy. Specifically, when comparing position and size, they should be relative to each other. If your drag target is positioned inside another clip, it will need to consider the parent positioning. I recommend always doing localToGlobal on coordinates when comparing them.
Example:
// Find the clip's top/left position in the global scope
var p = myContainer.localToGlobal(myClip.x, myClip.y);
// OR
// Find the clip's position in the global scope
var p = myClip.localToGlobal(0, 0);
Here's a bit of code that represents the general style I've been coding my site:
!function(){
window.ResultsGrid = Class.extend(function(){
this.constructor = function($container, options){
this.items = [];
this.$container = $($container);
this.options = $.extend({}, defaultOptions, options);
this.$grid = $(
'<div class="results-grid">\
<ul></ul>\
</div>'
)
.css("margin", -this.options.spacing / 2);
this.$list = this.$grid.find("ul");
this.$container.append(this.$grid);
};
this.setItems = function(datas) {
this.$grid.addClass("display-none");
this.clear();
for (var k in datas) this.addItem(datas[k]);
this.$grid.removeClass("display-none");
};
this.addItem = function(data) {
var width = this.options.columnWidth;
var height = this.options.rowHeight;
var padding = this.options.spacing / 2;
if (this.options.columns > 0) width = (this.$container.width() - this.options.columns * this.options.spacing) / this.options.columns;
if (this.options.rows > 0) height = (this.$container.height() - this.options.rows * this.options.spacing) / this.options.rows;
if (this.options.ratio > 0) height = width / this.options.ratio;
var item = new (this.options.class)(this.$list, {
data: data,
type: this.options.type,
width: width,
height: height,
padding: padding
});
this.items.push(item);
};
this.clear = function() {
for (var k in this.items) this.items[k].destroy();
this.items.length = 0;
};
this.destroy = function() {
this.clear();
this.$grid.find("*").off();
this.$grid.remove();
}
});
var defaultOptions = {
class: ResultsItem.Game,
type: ResultsItem.Game.COMPACT,
columns:1,
rows:0,
spacing: 10,
rowHeight: 80,
ratio: 0,
columnWidth: 0
};
}();
This is something I use for lists of items, it's just a base class so it looks fairly pointless.
On my homepage I have a few of these 'ResultsGrids' and in total I have about 100 items to be added. Each of these items calls append, addClass, css, etc. to their representative jquery object about 5 times, so that's a lot of HTML fiddling before it ever renders.
Problem is, there's quite a noticable time delay as I've just come to understand I'm accessing the DOM an unnecessary amount of times by calling methods like jquery.append for each item.
The obvious solution is to do one big append for each ResultsGrid by concatenating the html strings of each item, but I wonder if there's a middle ground between this and my current approach which will perform just as well, otherwise I'll have to rewrite a lot of code.
I like to start with a $("") and append bit by bit, but obviously this isn't good performance wise because it's constantly recalculating stuff, but I don't need to know the width, height and position of everything every step of the way. Ideally I'd like to tell it to not do anything to the DOM until I tell it to. If there's no way to do this with jquery, then I'd like a library that will allow me to do this.
I've had a brief look at js templating libraries but they don't look very enticing. Angular especially.
I don't have the big picture of what you're trying to do, but, from what I understand, you could try using jquery's $.detach() function.
this.setItems = function(datas) {
//store the parent of this grid
this.parent = this.$grid.parent();
this.$grid.detach();
this.$grid.addClass("display-none");
this.clear();
//here you add your html/etc to the grid
for (var k in datas) this.addItem(datas[k]);
//reattach the grid to the parent
this.parent.append(this.$grid);
this.$grid.removeClass("display-none");
};
Doing DOM manipulation on detached elements should be much faster and provide you with an in-between solution like you were thinking.
This is a performance tip that you could find here, along with some other useful ones.
Code:
http://jsfiddle.net/s4UQP/
^ Here is the best way to see the code and how it works with the divs
But here is the code anyway:
function move(from, to) {
document.getElementById('progress').innerHTML = '...';
from = parseInt(from,10);
to = parseInt(to,10);
tbc = document.getElementById(from);
before = document.getElementById(to);
containr = document.getElementById('alldivs');
neworder = 'Order: <select><option onclick="move(' + to + ',1)">1</option><option onclick="move(' + to + ',2)">2</option><option onclick="move(' + to + ',3)">3</option></select> <br>Send up | Send down<br>Bring to front (#1) | Send to back (#4)';
document.getElementById(from).getElementsByClassName('order')[0].innerHTML = neworder;
document.getElementById(from).getElementsByClassName('number')[0].innerHTML = to;
tempdiv = document.createElement('div');
tmphtml = document.getElementById(from).innerHTML;
tempdiv.className = 'holder';
tempdiv.innerHTML = tmphtml;
n = 0;
npieces = 4;
if (from < to) {
nochanges = to - from;
fromone = from + 1;
//alert(n+' '+to+' '+fromone);
for (n = fromone; n <= to; n++) {
//alert('down');
idnum = parseInt(document.getElementById(n).id,10);
//alert(idnum);
document.getElementById(n).getElementsByClassName('number')[0].innerHTML = (idnum - 1);
alert(document.getElementById(n).id);
document.getElementById(n).id = (idnum - 1);
//alert('down '+idnum+' to '+(idnum-1));
}
}
if (from > to) {
nochanges = from - to;
totone = to + 1;
for (n = to; n < from; n++) {
//alert('n is '+n+' going to '+to+' ends at '+totone);
//alert('up');
idnum = parseInt(document.getElementById(n).id,10);
//alert(idnum);
document.getElementById(n).getElementsByClassName('number')[0].innerHTML = (idnum + 1);
alert(document.getElementById(n).id);
document.getElementById(n).id = (idnum + 1);
//alert('up '+idnum+' to '+(idnum+1));
}
}
//tempdiv.id = 'span'+to;
if (from > to) {
containr.insertBefore(tempdiv, before);
}
if (from < to) {
before = to + 1;
containr.insertBefore(tempdiv, document.getElementById(before));
}
tbc.parentNode.removeChild(tbc);
tempdiv.id = to;
document.getElementById('progress').innerHTML = 'done';
}
The script works as you move a block (or div) up or down, but when you try to move a different block (e.g. the one at the top), it just switches around the first two blocks beneath it.
Could anyone give me any advice?
I don't know whether it's because of the order that the script was done in, or if it's something else. It's been confusing me for some time, and I'd really appreciate it if someone could look through it and give me some advice.
(I don't want to code it in jQuery, this is really just me trying to learn more JavaScript by coding something. If it's not the most efficient, secure, whatever, it's still just something with which I'm trying to teach myself JavaScript.)
Thank you for reading. (Please don't edit the JS Fiddle itself, but rather post any edits/improvements here. Thank you.)
[Edit: I'm not really writing a cliche sci-fi, they're just example divs because I couldn't think of anything better]
In the statement neworder =... you change the values of the onclick functions, but you only do this for the block that is about to be moved. The problem is that the other blocks also change positions. For instance, if you click on 'Send up' for block 2, then block 2 moves up to position 1 and block 1 moves down to position 2. But only the event handlers on block 2 are updated accordingly. So the next time you click on (what was originally) block 1, it will not behave correctly.
One solution would be to update the event handlers on all of the blocks that are affected every time one of them is moved. For instance, make a function called updateEventHandlers(blockNumber) and call it for all of the affected blocks.
However relying on IDs to indicate the position of a block and then fiddling with the IDs after they are moved can lead to all sorts of confusion. It is better either to keep an array or dictionary recording the positions of the blocks, or loop through them to determine their positions in the DOM each time you want to move them.
For instance the following code provides moveup, movedown and moveto functions using the latter method (it finds where the element is in the DOM and swaps it with the holder before or after). (JSFIDDLE)
function E(id) { return document.getElementById(id);}
var holders = document.getElementsByClassName('holder');
function moveup(id) {
for (var i = 0; i < holders.length - 1; i++) {
// Find the holder before the one we're interested in
if (holders[i + 1] == E(id)) {
// Swap their positions
E('alldivs').insertBefore(E(id), holders[i]);
break;
}
}
resetNumbers();
}
function movedown(id) {
for (var i = 1; i < holders.length; i++) {
// Find the holder after the one we're interested in
if (holders[i - 1] == E(id)) {
// Swap their positions
E('alldivs').insertBefore(holders[i], E(id));
break;
}
}
resetNumbers();
}
function moveto(id, position) {
if (position == holders.length) { // move to end
E('alldivs').appendChild(E(id));
}
else { // move before another holder
E('alldivs').insertBefore(E(id), holders[position - 1]);
}
resetNumbers();
}
function resetNumbers() {
// Reset all the numbers to reflect their current position
var numbers = document.getElementsByClassName('number');
for (var i = 0; i < numbers.length; i++) {
numbers[i].innerHTML = i + 1;
}
}
A few other points:
clicking on the selects in your original code won't do anything initially, because no event handler is assigned to it until after one of the elements has been moved
there is a missing </div> from the end of the html
it is good practice to declare variables using var somewhere in your code
appendChild and insertBefore remove a node from its current position in the DOM before appending/inserting it in its new position, so there is no need to remove the element explicitly.
having moveup and movedown functions is better than only having moveto, which requires you to insert the current, preceding and following positions into the html and refresh them every time a block is moved.
Okay so I am developing a WordPress theme. On the single post page I have a comments div which floats down the page using some jquery. I am also running a modal popup form to log in. This is completely fine on the single page when the #commentWrapper (selector for the jquery floating effect) exists. However on pages where there is no #commentWrapper to float, the modal form doesn't work. I pinned down the problem to this line in my general jQuery call (by removing each line and testing).
Call in general.js, very last call:
jQuery('#commentWrapper').stickyfloat({ duration: 300, easing : 'easeInQuad' });
Actual plugin it refers to:
$.fn.stickyfloat = function(options, lockBottom) {
var $obj = this;
var parentPaddingTop = parseInt($obj.parent().css('padding-top'));
var startOffset = $obj.parent().offset().top;
var opts = $.extend({ startOffset: startOffset, offsetY: parentPaddingTop, duration: 200, lockBottom:true }, options);
$obj.css({ position: 'absolute' });
if(opts.lockBottom){
var bottomPos = $obj.parent().height() - $obj.height() + parentPaddingTop; //get the maximum scrollTop value
if( bottomPos < 0 )
bottomPos = 0;
}
$(window).scroll(function () {
$obj.stop(); // stop all calculations on scroll event
var pastStartOffset = $(document).scrollTop() > opts.startOffset; // check if the window was scrolled down more than the start offset declared.
var objFartherThanTopPos = $obj.offset().top > startOffset; // check if the object is at it's top position (starting point)
var objBiggerThanWindow = $obj.outerHeight() < $(window).height(); // if the window size is smaller than the Obj size, then do not animate.
// if window scrolled down more than startOffset OR obj position is greater than
// the top position possible (+ offsetY) AND window size must be bigger than Obj size
if( (pastStartOffset || objFartherThanTopPos) && objBiggerThanWindow ){
var newpos = ($(document).scrollTop() -startOffset + opts.offsetY );
if ( newpos > bottomPos )
newpos = bottomPos;
if ( $(document).scrollTop() < opts.startOffset ) // if window scrolled < starting offset, then reset Obj position (opts.offsetY);
newpos = parentPaddingTop;
$obj.animate({ top: newpos }, opts.duration );
}
});
};
If I add an if command to see if the selector exists, it all works. I would like to know what the problem is for future website however.
Well, your stickyfloat() method assumes many things, like always being called on a jQuery object that contains at least one element, or that element always having a parent. For instance, consider the following code:
var $obj = this;
// ...
var startOffset = $obj.parent().offset().top;
If the jQuery object your method is called on is empty, or if its first element has no parent (the method was called on $("html")), the code will fail because parent().offset() will be null.
If you want your method to be more robust, you should not assume anything about the object it's called on. A good first step is to make the method chainable, which is always beneficial to your users and will get rid of the first problem. The recommended way of doing that is:
$.fn.stickyfloat = function(options, lockBottom) {
return this.each(function() {
var $obj = $(this);
// The rest of your code.
});
};
Since the code now runs sequentially on each element (if any) through an anonymous function, testing for the parent element's existence can be dealt with by returning early:
var $obj = $(this);
var $parent = $obj.parent();
if (!$parent.length) {
return; // No parent, continue with next element, if any.
}
// Parent element is safe to use.
var parentPaddingTop = parseInt($parent.css('padding-top'));
var startOffset = $parent.offset().top;