Slide Feature: setProperty not working in IE - javascript

I'm working on a slide feature and it works great on all browsers except IE of course.
I firstly thought it's because of Math.sign but I've added a polyfill for it and it solve the error, but the slider still didn't work. Then I investigated a bit more and I think it's because of this syntax
_C.style.setProperty('--i', num);
which is used to set a value and use it in CSS like this:
transform: translate(calc(var(--i, 0)/var(--n)*var(--n)*-100%));
function move(e) {
document.getElementsByClassName('checked')[0].classList.remove('checked')
if(x0 || x0 === 0) {
if (!Math.sign) {
Math.sign = function(x) {
return ((x > 0) - (x < 0)) || +x;
}
}
let dx = unify(e).clientX - x0, s = Math.sign(dx);
if((i > 0 || s < 0) && (i < N - 1 || s > 0))
_C.style.setProperty('--i', i -= s);
let circles = document.getElementsByClassName('testimonial-circle')
let circle = circles[i]
circle.classList.add('checked')
x0 = null
}
};
The idea is to click on a bubble and slide to the specific "testimonial"

Related

How to programmatically tell if two absolutely positioned elements overlap?

I don't think there's any such method in the DOM API like element.doesOverlap(otherElement), so I think I have to calculate this by hand, right? Not sure if there are any shortcuts.
If not, what is the method for this? It seems like there's so many ways something could overlap....it would be so many conditionals. Is there a concise way of writing this?
In pseudo code, I have this:
if (
((A.top < B.bottom && A.top >= B.top)
|| (A.bottom > B.top && A.bottom <= B.bottom))
&&
((A.left < B.right && A.left >= B.left)
|| (A.right > B.left && A.right <= B.right))) {
// elements A and B do overlap
}
^Is this the simplest way?
This is essentially and x,y comparison problem. You essentially need to compare the two element by there x,y positions at all boundaries ( top, right, bottom and left ) if they overlap anywhere.
A simple method would be, to test that they don't overlap.
Two items could be considered to overlap if none of the following are true:
- box1.right < box2.left // too far left
- box1.left > box2.right // too far right
- box1.bottom < box2.top // too far above
- box1.top > box2.bottom // too far below
Only really a slight change to what you had.
function checkOverlap(elm1, elm2) {
e1 = elm1.getBoundingClientRect();
e2 = elm2.getBoundingClientRect();
return e1.x <= e2.x && e2.x < e1.x + e1.width &&
e1.y <= e2.y && e2.y < e1.y + e1.height;
}
window.onload = function() {
var a = document.getElementById('a');
var b = document.getElementById('b');
var c = document.getElementById('c');
console.log("a & b: "+checkOverlap(a,b));
console.log("a & c: "+checkOverlap(a,c));
console.log("b & c: "+checkOverlap(b,c));
}
<div id="a" style="width:120px;height:120px;background:rgba(12,21,12,0.5)">a</div>
<div id="b" style="position:relative;top:-30px;width:120px;height:120px;background:rgba(121,211,121,0.5)">b</div>
<div id="c" style="position:relative;top:-240px;left:120px;width:120px;height:120px;background:rgba(211,211,121,0.5)">c</div>
There isn't an easier way. The correct code is this, covering all possible ways two elements can overlap:
const doElementsOverlap = (elementA: any, elementB: any) => {
const A = elementA.getBoundingClientRect();
const B = elementB.getBoundingClientRect();
return (
((A.top < B.bottom && A.top >= B.top)
|| (A.bottom > B.top && A.bottom <= B.bottom)
|| (A.bottom >= B.bottom && A.top <= B.top))
&&
((A.left < B.right && A.left >= B.left)
|| (A.right > B.left && A.right <= B.right)
|| (A.left < B.left && A.right > B.right))
);
};

How to reset everything in my javascript game

I built an extensive minesweeper game in JS and I'm trying to implement an effective way to restart the game on click but I'm coming up short. Right now I'm just making the entire page reload on click but that's not what I want to happen. The way I built the game, everything is placed on load, so I'm not sure how to approach this without refactoring all of my code. I tried creating a function that resets all global variables, removes all the divs I created before and then calls a function which I created to just wrap all my code and do it all over again. This approach removed the divs but did not place them again.
This is my primary function
function createBoard() {
const bombsArray = Array(bombAmount).fill('bomb')
const emptyArray = Array(width * height - bombAmount).fill('valid')
const gameArray = emptyArray.concat(bombsArray)
// --Fisher–Yates shuffle algorithm--
const getRandomValue = (i, N) => Math.floor(Math.random() * (N - i) + i)
gameArray.forEach((elem, i, arr, j = getRandomValue(i, arr.length)) => [arr[i], arr[j]] = [arr[j], arr[i]])
// --- create squares ---
for (let i = 0; i < width * height; i++) {
const square = document.createElement('div')
square.setAttribute('id', i)
square.classList.add(gameArray[i])
grid.appendChild(square)
squares.push(square)
square.addEventListener('click', function () {
click(square)
})
square.oncontextmenu = function (e) {
e.preventDefault()
addFlag(square)
}
}
//add numbers
for (let i = 0; i < squares.length; i++) {
let total = 0
const isLeftEdge = (i % width === 0)
const isRightEdge = (i % width === width - 1)
if (squares[i].classList.contains('valid')) {
//left
if (i > 0 && !isLeftEdge && squares[i - 1].classList.contains('bomb')) total++
//top right
if (i > 9 && !isRightEdge && squares[i + 1 - width].classList.contains('bomb')) total++
//top
if (i > 10 && squares[i - width].classList.contains('bomb')) total++
//top left
if (i > 11 && !isLeftEdge && squares[i - 1 - width].classList.contains('bomb')) total++
//right
if (i < 129 && !isRightEdge && squares[i + 1].classList.contains('bomb')) total++
//bottom left
if (i < 120 && !isLeftEdge && squares[i - 1 + width].classList.contains('bomb')) total++
//bottom right
if (i < 119 && !isRightEdge && squares[i + 1 + width].classList.contains('bomb')) total++
//bottom
if (i <= 119 && squares[i + width].classList.contains('bomb')) total++
squares[i].setAttribute('data', total)
}
}
}
createBoard()
Really I just want to be able to clear on click the divs this function creates and then make them again. When I try this:
function resetGame() {
width = 10
height = 13
bombAmount = 20
squares = []
isGameOver = false
flags = 0
grid.remove('div')
createBoard()
}
This effectively removes the grid squares created on load but it doesn't create them again. I want to be able to run that initial function again. How can I do that?
Here's a codepen
You are removing the .grid container, instead of
grid.remove("div");
Use the following statement to remove all content of the container
grid.innerHTML = "";
Pen

switch, else if & or (Beginner Q)

I have a function which appears to always be returning true on the first condition irrelevant of input. I have been reading about using switch versus else if as I have 16 conditions to check and I want to ensure I have working 'best practice'
Can I achieve the same thing using both options:
function lsaupdateTotals() {
var x = variablename.value;
var y = variablename2.value;
if (x = 1) || (y = y > 1 && y < 280) {
rdlsa = 7.45;
} else if (x = 2 || (y = y > 281 && y < 460)) {
rdlsa = 11.65;
/ or switch: /
switch (x) {
case 1:
case y > 1:
case y < 280:
y = 7.45;
break;
}
There are several problem in your code:
In javascript, to compare 2 numbers (or strings) you have to use the syntax ===, so if (x = 1) should became if (x === '1') (as I'm expecting x is a string).
The if condition should be in a parenthesis: if (x = 1) || (y = y > 1 && y < 280) { => if ((x === 1) || (y === y > 1 && y < 280)) {
It's not clear what you mean with y = y > 1 (or y === y > 1) in first if (second parenthesis)
In switch/case syntax you cannot use y>1, please refer to switch/case syntax (internet is full of documentation)
When you put an assignment into the if (using = instead of ===) the if statement consider true the condition if the value after the = is not null, 0, empty string, false or undefined, for this reason when you write if(x=1){ the condition is always true.
You've got a few issues in your code:
You're using = instead of == or === to check equality in your if statements. A single = sign always means "set equal to", not "is equal to?".
In your if statement your parens make things a bit ambiguous. It's possible that this works just fine, but wrapping the entire question in parens is guaranteed to work as intended while also being completely clear.
Rewriting your if statement according to the above:
if (x == 1 || (y == y > 1 && y < 280)) {
rdlsa = 7.45;
}
else if (x == 2 || (y == y > 281 && y < 460)) {
rdlsa = 11.65;
}
(EDIT: Note that the y == y > 1 part is almost definitely not doing what you want it to. That's asking "is y the same thing as y > 1?")
In your switch, think of the value in each case as being a place holder for what you're putting into it. So in your example, using y>1 doesn't make sense to evaluate against x, because it's asking if x is *equal to y>1, but y>1 is always true or false and is independent of x.
you are getting the conditions wrong.
Please replace your code with the below lines
function lsaupdateTotals() {
var x = variablename.value;
var y = variablename2.value;
if ((x == 1) || (y > 1 && y < 280)) {
rdlsa = 7.45;
} else if ((x == 2) || (y > 281 && y < 460)) {
rdlsa = 11.65;
}
/ or switch: /
switch (x) {
case 1:
case y > 1:
case y < 280:
y = 7.45;
break;
}

trying to get offsetTop on window.scroll and then traverse the DOM according to the touched slide

I am creating one page site and then trying to get offsetTop on window.scroll, by which i want to traverse the DOM according to the slide.
a lot of tries.. feeling dumb now..
if anyone can help, would be highly appreciable.
thanks
here is the code and fiddle URL:
$(window).scroll(function () {
var y = $(window).scrollTop(),
a = $('#first').offset().top - 200,
b = $('#second').offset().top - 200,
c = $('#third').offset().top - 200,
d = $('#fourth').offset().top - 200;
if (y > a) {
$('h1').html('This is First Slide');
}
if (y > b) {
$('h1').html('This is Second Slide');
}
if (y > c) {
$('h1').html('This is Third Slide');
}
if (y > d) {
$('h1').html('This is Third Slide');
}
else{
$('h1').html('No heading');
}
});
http://jsfiddle.net/A8Hmr/9/
Your logic is correct it's just a miss with the ifs.
I will show the code and explain:
var a = $('#first').offset().top - 200,
b = $('#second').offset().top - 200,
c = $('#third').offset().top - 200,
d = $('#fourth').offset().top - 200;
$(window).scroll(function () {
var y = $(window).scrollTop();
if (y > a && y < b) {
$('h1').text('This is First Slide');
}
else if (y > b && y < c) {
$('h1').text('This is Second Slide');
}
else if (y > c && y < d) {
$('h1').text('This is Third Slide');
}
else if (y > d) {
$('h1').text('This is Third Slide');
}
else{
$('h1').text('No heading');
}
});
Demo
1) You don't need to take the offset of the slides on every scroll, since they don't change, you can put them outside of the scroll event, that way it will improve the performance.
2) The problem in the code was the if. Since they were all ifs (and not if/else if) statements, all of theme were checked if they were true. Meaning that if the first one was true the next one will not be true and it will enter in the else statement automaticaly overwriting the if that was true.
So you have to make them if/else if and since once y > a become true it will always be true (untill it goes to y < a) you must have an additional condition if y < b meaning if y is less then the next slide. Ofcourse once again you can use only if/else but what is the point in checking 5 things if only one is correct ? Performance should be a main thing in every js code. ;)
Version 2:
(function(){
var a = $('#first').offset().top - 200,
b = $('#second').offset().top - 200,
c = $('#third').offset().top - 200,
d = $('#fourth').offset().top - 200,
h1 = $('h1'),
textChange = ['No heading','This is First Slide','This is Second Slide','This is Third Slide', 'This is Third Slide']
$(window).scroll(function () {
var y = $(window).scrollTop();
if (y > a && y < b && h1.text() != textChange[1]) {
h1.text(textChange[1]);
}
else if (y > b && y < c && h1.text() != textChange[2]) {
h1.text(textChange[2]);
}
else if (y > c && y < d && h1.text() != textChange[3]) {
h1.text(textChange[3]);
}
else if (y > d && h1.text() != textChange[4]) {
h1.text(textChange[4]);
}
else if(y <= a && h1.text() != textChange[0]){
h1.text(textChange[0]);
}
});
})();
Demo
What change here?
1) I wrapped the whole thing in self invoking anonymous function (since it's not a good practice to have global variables).
2) We made a variable outside the scroll event that will hold the h1 so we don't have to go in the dom on every scroll event.
3) We made an array that will hold the text that will change. (and updated the values in the text scroll)
4) We changed the if condition in the if statement to check if the text is already the same so we don't have to change it again. So now it will fire only once instead of firing every time we scroll.
5) We changed the else to else if since it would enter once the text is the same an jump to the else.
Pretty much that should increase the performance a lot.

Creating 2d platforms using JavaScript

I'm developing a HTML5 Canvas game using EaselJS and I've written a function that allows me to create "blocks" just by setting one or more images, size and position.
and by "blocks", what I mean is:
I'm doing this using two methods:
First method:
With this method the blocks are created in the available space inside the location I've set, using the images randomly.
Second method:
The blocks are created inside the location I've set using specific images for the top left corner, top side, top right corner, left side, center, right side, bottom left corner, bottom side and bottom right corner, and there can be more than a single image for each one of those parts (so the system uses a random one to avoid repeating the same image multiple times).
Ok, but what's the problem?
This function uses a zillion 77 lines (131 lines counting with the collision-detection-related part)! I know there's a better way of doing this, that will take about a half or less lines than it's taking now, but I don't know how to do it and when someone show me, I'll use the "right way" for the rest of my life. Can you help me?
What I want:
A possible way to use less lines is to use a single "method" that allows me to create blocks that are compound by blocks that are compound by the 9-or-more images (I just don't know how to do it, and I know it's difficult to understand. Try to imagine the third image being used 9 times). // This part of the question makes it on-topic!
Note that this question isn't subjective, since the goal here is to use less lines, and I'm not using the EaselJS tag because the question isn't EaselJS-specific, anyone with JavaScript knowledge can answer me.
Here's my incredibly big JavaScript function:
var Graphic = function (src, blockWidth, blockHeight) {
return {
createBlockAt: function (x, y, blockGroupWidth, blockGroupHeight, clsdir, alpha) {
for (var blockY = 0; blockY < blockGroupHeight / blockHeight; blockY++) {
for (var blockX = 0; blockX < blockGroupWidth / blockWidth; blockX++) {
var obj = new createjs.Bitmap(src[Math.floor(Math.random() * src.length)]);
obj.width = blockWidth;
obj.height = blockHeight;
if (typeof alpha !== 'undefined') {
obj.alpha = alpha; // While debugging this can be used to check if a block was made over another block.
}
obj.x = Math.round(x + (blockWidth * blockX));
obj.y = Math.round(y + (blockHeight * blockY));
stage.addChild(obj);
}
}
}
}
}
var complexBlock = function (topLeft, topCenter, topRight, middleLeft, middleCenter, middleRight, bottomLeft, bottomCenter, bottomRight, blockWidth, blockHeight) {
return {
createBlockAt: function (x, y, blockGroupWidth, blockGroupHeight, clsdir, alpha) {
for (var blockY = 0; blockY < blockGroupHeight / blockHeight; blockY++) {
for (var blockX = 0; blockX < blockGroupWidth / blockWidth; blockX++) {
if (blockY == 0 && blockX == 0) {
var obj = new createjs.Bitmap(topLeft[Math.floor(Math.random() * topLeft.length)]);
}
if (blockY == 0 && blockX != 0 && blockX != (blockGroupWidth / blockWidth - 1)) {
var obj = new createjs.Bitmap(topCenter[Math.floor(Math.random() * topCenter.length)]);
}
if (blockY == 0 && blockX == (blockGroupWidth / blockWidth - 1)) {
var obj = new createjs.Bitmap(topRight[Math.floor(Math.random() * topRight.length)]);
}
if (blockY != 0 && blockY != (blockGroupHeight / blockHeight - 1) && blockX == 0) {
var obj = new createjs.Bitmap(middleLeft[Math.floor(Math.random() * middleLeft.length)]);
}
if (blockY != 0 && blockY != (blockGroupHeight / blockHeight - 1) && blockX != 0 && blockX != (blockGroupWidth / blockWidth - 1)) {
var obj = new createjs.Bitmap(middleCenter[Math.floor(Math.random() * middleCenter.length)]);
}
if (blockY != 0 && blockY != (blockGroupHeight / blockHeight - 1) && blockX == (blockGroupWidth / blockWidth - 1)) {
var obj = new createjs.Bitmap(middleRight[Math.floor(Math.random() * middleRight.length)]);
}
if (blockY == (blockGroupHeight / blockHeight - 1) && blockX == 0) {
var obj = new createjs.Bitmap(bottomLeft[Math.floor(Math.random() * bottomLeft.length)]);
}
if (blockY == (blockGroupHeight / blockHeight - 1) && blockX != 0 && blockX != (blockGroupWidth / blockWidth - 1)) {
var obj = new createjs.Bitmap(bottomCenter[Math.floor(Math.random() * bottomCenter.length)]);
}
if (blockY == (blockGroupHeight / blockHeight - 1) && blockX == (blockGroupWidth / blockWidth - 1)) {
var obj = new createjs.Bitmap(bottomRight[Math.floor(Math.random() * bottomRight.length)]);
}
obj.width = blockWidth;
obj.height = blockHeight;
if (typeof alpha !== 'undefined') {
obj.alpha = alpha; // While debugging this can be used to check if a block was made over another block.
}
obj.x = Math.round(x + (blockWidth * blockX));
obj.y = Math.round(y + (blockHeight * blockY));
stage.addChild(obj);
}
}
}
}
}
var bigDirt = complexBlock(["http://i.imgur.com/DLwZMwJ.png"], ["http://i.imgur.com/UJn3Mtb.png"], ["http://i.imgur.com/AC2GFM2.png"], ["http://i.imgur.com/iH6wFj0.png"], ["http://i.imgur.com/wDSNzyc.png", "http://i.imgur.com/NUPhXaa.png"], ["http://i.imgur.com/b9vCjrO.png"], ["http://i.imgur.com/hNumqPG.png"], ["http://i.imgur.com/zXvJECc.png"], ["http://i.imgur.com/Whp7EuL.png"], 40, 40);
bigDirt.createBlockAt(0, 0, 40*3, 40*3);
Okay... Lots of code here, how do I test?
Here we go: JSFiddle
I don't see an easy way to reduce the number of lines given the nine possible branches, but you can substantially reduce the repetition in your code:
function randomImage(arr) {
return new createjs.Bitmap(arr[Math.floor(Math.random() * arr.length)]);
}
if (blockY == 0 && blockX == 0) {
var obj = randomImage(topLeft);
} // etc
Re: the nine possible branches, you should note that they are mutually exclusive, so should be using else if instead of just if, and that they are also naturally grouped in threes, suggesting that they should be nested.
EDIT in fact, there is a way to reduce the function size a lot. Note that for X and Y you have three options each (nine in total). It is possible to encode which image array you want based on a two-dimensional lookup table:
var blocksHigh = blockGroupHeight / blockHeight;
var blocksWide = blockGroupWidth / blockWidth;
var blockSelector = [
[topLeft, topCenter, topRight],
[middleLeft, middleCenter, middleRight],
[bottomLeft, bottomCenter, bottomRight]
];
for (var blockY = 0; blockY < blocksHigh; blockY++) {
var blockSY = (blockY == 0) ? 0 : blockY < (blocksHigh - 1) ? 1 : 2;
for (var blockX = 0; blockX < blocksWide; blockX++) {
var blockSX = (blockY == 0) ? 0 : blockY < (blocksWide - 1) ? 1 : 2;
var array = blockSelector[blockSY][blockSX];
var obj = randomImage(array);
...
}
}
Note the definitions of blocksHigh and blocksWide outside of the loop to reduce expensive repeated division operations.
See http://jsfiddle.net/alnitak/Kpj3E/
Ok, it's almost a year later now and I decided to come back here to improve the existing answers. Alnitak's suggestion on creating a "2-dimensional lookup table" was genius, but there's a even better way of doing what I was asking for.
Sprite Sheets
The problem core is the need for picking lots of separated images and merge them in order to create a bigger mosaic. To solve this, I've merged all images into a sprite sheet. Then, with EaselJS, I've separated each part of the platform (topLeft, topCenter, etc) in multiple animations, and alternative images of the same platform part that would be used randomly are inserted within it's default part animation, as an array (so topLeft can be five images that are used randomly).
This was achieved by making a class that creates an EaselJS container object, puts the sprite sheet inside this container, moves the sprite sheet to the correct position, caches the frame and updates the container cache using the "source-overlay" compositeOperation — which puts the current cache over the last one — then it does this again until the platform is finished.
My collision detection system is then applied to the container.
Here's the resulting JavaScript code:
createMosaic = function (oArgs) { // Required arguments: source: String, width: Int, height: Int, frameLabels: Object
oArgs.repeatX = oArgs.repeatX || 1;
oArgs.repeatY = oArgs.repeatY || 1;
this.self = new createjs.Container();
this.self.set({
x: oArgs.x || 0,
y: oArgs.y || 0,
width: ((oArgs.columnWidth || oArgs.width) * oArgs.repeatX) + oArgs.margin[1] + oArgs.margin[2],
height: ((oArgs.lineHeight || oArgs.height) * oArgs.repeatY) + oArgs.margin[0] + oArgs.margin[3],
weight: (oArgs.weight || 20) * (oArgs.repeatX * oArgs.repeatY)
}).set(oArgs.customProperties || {});
this.self.cache(
0, 0,
this.self.width, this.self.height
);
var _bmp = new createjs.Bitmap(oArgs.source);
_bmp.filters = oArgs.filters || [];
_bmp.cache(0, 0, _bmp.image.width, _bmp.image.height);
var spriteSheet = new createjs.SpriteSheet({
images: [_bmp.cacheCanvas],
frames: {width: oArgs.width, height: oArgs.height},
animations: oArgs.frameLabels
});
var sprite = new createjs.Sprite(spriteSheet);
this.self.addChild(sprite);
for (var hl = 0; hl < oArgs.repeatY; hl++) {
for (var vl = 0; vl < oArgs.repeatX; vl++) {
var _yid = (hl < 1) ? "top" : (hl < oArgs.repeatY - 1) ? "middle" : "bottom";
var _xid = (vl < 1) ? "Left" : (vl < oArgs.repeatX - 1) ? "Center" : "Right";
if(typeof oArgs.frameLabels[_yid + _xid] === "undefined"){
oArgs.frameLabels[_yid + _xid] = oArgs.frameLabels["topLeft"];
} // Case the expected frameLabel animation is missing, it will default to "topLeft"
sprite.gotoAndStop(_yid + _xid);
if (utils.getRandomArbitrary(0, 1) <= (oArgs.alternativeTileProbability || 0) && oArgs.frameLabels[_yid + _xid].length > 1) { // If there are multiple frames in the current frameLabels animation, this code choses a random one based on probability
var _randomPieceFrame = oArgs.frameLabels[_yid + _xid][utils.getRandomInt(1, oArgs.frameLabels[_yid + _xid].length - 1)];
sprite.gotoAndStop(_randomPieceFrame);
}
sprite.set({x: vl * (oArgs.columnWidth || oArgs.width), y: hl * (oArgs.lineHeight || oArgs.height)});
this.self.updateCache("source-overlay");
}
}
this.self.removeChild(sprite);
awake.container.addChild(this.self);
};
Usage:
createMosaic({
source: "path/to/spritesheet.png",
width: 20,
height: 20,
frameLabels: {
topLeft: 0, topCenter: 1, topRight: 3,
middleLeft: 4, middleCenter: [5, 6, 9, 10], middleRight: 7,
bottomLeft: 12, bottomCenter: 13, bottomRight: 15
},
x: 100,
y: 100,
repeatX: 30,
repeatY: 15,
alternativeTileProbability: 75 / 100
});
I would recommend using the "createMosaic" as a function returned by a constructor that passes the required arguments to it, so you'll not need to write the source image path, width, height and frameLabels every time you want to create a dirt platform, for example.
Also, this answer may have more LoC than the others that came before, but it's made this way in order to have more structure.

Categories