Simulating shuffle in js - javascript

All,
I have three cards which can be shuffled by the user, upon hover, the target card pops to the top, the last card on top should sit in the second position. While with the code below, I can have this effect in one direction (left to right), I am struggling to come up with logic & code for getting the effect to work in both directions without having to write multiple scenarios in js (which doesnt sound like very good logic).
Hopefully the demo will do a better explanation.
Code:
$(".cBBTemplates").on (
{
hover: function (e)
{
var aBBTemplates = document.getElementsByClassName ("cBBTemplates");
var i = 2;
while (i < aBBTemplates.length && i >= 0)
{
var eCurVar = aBBTemplates[i];
if (eCurVar === e.target)
{
eCurVar.style.zIndex = 3;
} else if (eCurVar.style.zIndex === 3) {
console.log (eCurVar);
eCurVar.style.zIndex = 3-1;
} else
{
eCurVar.style.zIndex = i;
}
i--;
}
}
});

Try this:
$(function(){
var current = 2;
$(".cBBTemplates").on (
{
hover: function ()
{
var target = this,
newCurrent, templates = $(".cBBTemplates");
templates.each(
function(idx){
if(this === target){
newCurrent = idx;
}
});
if(newCurrent === current){return;}
templates.each(function(index){
var zIndex = 0;
if(this === target) {
zIndex = 2;
}
else if (index == current) {
zIndex = 1;
}
$(this).css('zIndex', zIndex);
});
current = newCurrent;
}
});
});

Related

Why can't I clear this timer in JavaScript?

There is an object.
There is this method that initializes the timer in another method within the same object.
initstep1() {
var totAns = TriviaGame.corAnswered + TriviaGame.incorAnswered;
//for (let v=0;v<TriviaGame.arrayOfSelected.length;v++){TriviaGame.arrayOfSelected.pop();}
//for (let u=0;u<TriviaGame.arrayOfIntervals.length;u++){clearInterval(TriviaGame.arrayOfIntervals[u]);TriviaGame.arrayOfIntervals.pop();}
$("#maincontact0").css("display", "none");
$("#maincontact2").css("display", "none");
$("#maincontact1").css("display", "flex");
if (totAns != 10) {
TriviaGame.populatePromptContent();
} else {
TriviaGame.initstep3();
}
if (TriviaGame.corAnswered == 0 && TriviaGame.incorAnswered == 0) {
TriviaGame.giveQuestionsClickEvents();
TriviaGame.giveAnswersClickEvents();
}
$("#maincontact1qr").text() == 30;
TriviaGame.timerOnTheRight();
}
It's called timerOnTheRight...
Here it is...
Never gets cleared no matter what I do.
timerOnTheRight() {
//for (let u=0;u<TriviaGame.arrayOfIntervals.length;u++){clearInterval(TriviaGame.arrayOfIntervals[u]);TriviaGame.arrayOfIntervals.pop();}
console.log(TriviaGame.arrayOfIntervals);
let countDown1 = 30;
var thisVeryTimer = setInterval(function() {
countDown1--;
if ($("#maincontact1qr").text() != 1) {
$("#maincontact1qr").text(countDown1);
}
if ($("#maincontact1qr").text() < 11) {
$("#maincontact1qr").css("color", "orange");
}
if ($("#maincontact1qr").text() < 4) {
$("#maincontact1qr").css("color", "red");
}
if ($("#maincontact1qr").text() == 1) {
TriviaGame.arrayOfCurrent[0].timespent = "Yes";
clearInterval(thisVeryTimer);
TriviaGame.initstep2();
}
}, 600);
}
Make sure the conditional is correct, and the inside the conditional block is working when conditioning is met. You should console out thisVeryTimer to make sure it is the same interval id.
Another issue should be the scope of the variable.
clearInterval(thisVeryTimer);
Try to move the above code out of the interval block.

Want JavaScript script to run when the Div is in the viewport

I want to run a JS script when a particular div comes into the viewport/is visible.
The div will always be visible, but when the user scrolls it in to view.
I have created a JSFiddle with the example:
Example http://jsfiddle.net/sv8boe9u/21/
JS
consoleText(['HELLO,', 'HERE IS A BIT ABOUT ME,', 'ENJOY!'], 'text', ['#333', '#333', '#333']);
function consoleText(words, id, colors) {
"use strict";
if (colors === undefined) {
colors = ['#fff'];
}
var visible = true;
var con = document.getElementById('console');
var letterCount = 1;
var x = 1;
var waiting = false;
var target = document.getElementById(id);
target.setAttribute('style', 'color:' + colors[0]);
window.setInterval(function() {
if (letterCount === 0 && waiting === false) {
waiting = true;
target.innerHTML = words[0].substring(0, letterCount);
window.setTimeout(function() {
var usedColor = colors.shift();
colors.push(usedColor);
var usedWord = words.shift();
words.push(usedWord);
x = 1;
target.setAttribute('style', 'color:' + colors[0]);
letterCount += x;
waiting = false;
}, 1000);
} else if (letterCount === words[0].length + 1 && waiting === false) {
waiting = true;
window.setTimeout(function() {
x = -1;
letterCount += x;
waiting = false;
}, 1000);
} else if (waiting === false) {
target.innerHTML = words[0].substring(0, letterCount);
letterCount += x;
}
}, 120);
window.setInterval(function() {
if (visible === true) {
con.className = 'console-underscore hidden';
visible = false;
} else {
con.className = 'console-underscore';
visible = true;
}
}, 400);
}
To clarify, I want it to start at 'Hello' when it is actually in viewport. Any ideas?
Thanks.
Using the jQuery scroll() and scrollTop() functions you can specify a height in px that triggers another function when reached such as:
$(window).scroll(function () {
if ($(this).scrollTop() >= 50) { // If page is scrolled more than 50px
doSomething(); // call this function
}
});
jQuery Scroll and Scroll Top
you can use the scroll function ..like create a function that runs until wen you reach that element u are targeting.. then call the alert('hello');
window.addEventListener('scroll', (e) => {
console.log(window.scrollY)
if (window.scrollY == 705) {
alert('got ya')
// do your stuff here boss
}
})
you should make sure to find that scroll position wen yo element comes into view and then put it wr 705 is.. hope this helps ya

clear javascript : determine which link was clicked

I have a cycle of links and I determined click event on them. And I want to define if navbar[1].clicked == true {doing something} else if navbar[2].cliked == true {doing something} etc. "By if else in " reveal functional callbackFn".
Here is the code:
var navbar = document.getElementById("navbar").getElementsByTagName("a");
for (var i = 0; i < navbar.length; i++) {
navbar[i].addEventListener('click', function() { reveal('top'); });
}
function reveal(direction) {
callbackFn = function() {
// this is the part where is running the turning of pages
classie.remove(pages[currentPage], 'page--current');
if (navbar[1].clicked == true) {
currentPage = 0;
} else if(navbar[1].clicked == true) {
currentPage = 1;
} else if(navbar[2].clicked == true) {
currentPage = 2;
} else if(navbar[3].clicked == true) {
currentPage = 3;
} else if(navbar[4].clicked == true) {
currentPage = 4;
};
classie.add(pages[currentPage], 'page--current');
};
}
This is typically a problem of closure.
You can make the following change
Here the call back function of the addEventListener is an IIFE, & in the reveal function pass the value of i
var navbar = document.getElementById("navbar").getElementsByTagName("a");
for (var i = 0; i < navbar.length; i++) {
navbar[i].addEventListener('click', (function(x) {
reveal('top',x);
}(i))};
}
In this function you will have access to
function reveal(direction,index) {
// not sure what this function is mean by, but you will have the value of `i` which is denote the clicked element
callbackFn = function() {
// this is the part where is running the turning of pages
classie.remove(pages[currentPage], 'page--current');
if (index == 1) {
currentPage = 0;
} else if (index == 1) {
currentPage = 1;
} else if (index == 2) {
currentPage = 2;
} else if (index == 3) {
currentPage = 3;
} else if (index == 4) {
currentPage = 4;
};
classie.add(pages[currentPage], 'page--current');
};
}
Here is the solution in my case.
Thank you brk for helping in any case, thanks again.
// determine clicked item
var n;
$('#navbar a').click(function(){
if($(this).attr('id') == 'a') {
n = 0;
} else if($(this).attr('id') == 'b') {
n = 1;
} else if($(this).attr('id') == 'c') {
n = 2;
} else if($(this).attr('id') == 'd') {
n = 3;
} else if($(this).attr('id') == 'e') {
n = 4;
};
});
var pages = [].slice.call(document.querySelectorAll('.pages > .page')),
currentPage = 0,
revealerOpts = {
// the layers are the elements that move from the sides
nmbLayers : 3,
// bg color of each layer
bgcolor : ['#52b7b9', '#ffffff', '#53b7eb'],
// effect classname
effect : 'anim--effect-3'
};
revealer = new Revealer(revealerOpts);
// clicking the page nav
document.querySelector("#a").addEventListener('click', function() { reveal('cornertopleft'); });
document.querySelector("#b").addEventListener('click', function() { reveal('bottom'); });
document.querySelector("#c").addEventListener('click', function() { reveal('left'); });
document.querySelector("#d").addEventListener('click', function() { reveal('right'); });
document.querySelector("#e").addEventListener('click', function() { reveal('top'); });
// moving clicked item's `n` into the function
function reveal(direction) {
var callbackTime = 750;
callbackFn = function() {
classie.remove(pages[currentPage], 'page--current');
currentPage = n;
classie.add(pages[currentPage], 'page--current');
};
revealer.reveal(direction, callbackTime, callbackFn);
}

Is this text snipper mini-plugin of mine efficient?

I am working on creating a WordPress plugin to snip overflowing multiline text and add "...". It's pretty basic for now, but I am using some for loops and am just wondering if this is the most efficient way to go about doing this without clogging resources.
(function($) {
//Praveen Prasad "HasScrollBar" function adapted http://stackoverflow.com/questions/7341865/checking-if-jquery-is-loaded-using-javascript
$.fn.hasOverflow = function() {
var elm = $(this);
var hasOverflow = false;
if ( elm.clientHeight < elm.scrollHeight ) {
hasOverflow = true;
}
return hasOverflow;
}
//http://www.mail-archive.com/discuss#jquery.com/msg04261.html
jQuery.fn.reverse = [].reverse;
$(document).ready( function() {
if ( $('.' + snipClass).length > 0 ) {
var count = 0;
for (var i = 0; i < 10000; i++) {
$('.' + snipClass).each( function () {
var el = this;
//Check for overflows
if ( el.clientHeight < el.scrollHeight) {
if ($(this).children().length > 0) { //Handle child elements
$("> *", this).reverse().each( function () {
for (var j = 0; j < 10000; j++) {
if ( $(this).text() != "" && el.clientHeight < el.scrollHeight ) {
$(this).text($(this).text().substring(0,$(this).text().length - 1));
} else {
break;
}
}
});
} else { //Handle elements with no children
$(this).text($(this).text().substring(0,$(this).text().length - 1));
}
} else { //Add '...'
count++;
}
});
//Add ... once finished
if ( count >= $('.' + snipClass).length) {
$('.' + snipClass).each( function () {
var el = this;
if ($(this).children().length > 0) { //Handle child elements
$("> *", this).reverse().each( function () {
if ( $(this).text() != "" ) {
$(this).text($(this).text().substring(0, $(this).text().length - 3) + "...");
}
});
} else { //Handle elements with no children
$(this).text($(this).text().substring(0, $(this).text().length - 3) + "...");
}
});
break;
}
}
}
});
}(jQuery));
Brief explanation: "snipClass" is the value from the textfield in the plugin's settings page. If the element in question has a wrapper that is say 200px, but its text overflows it, I keep trimming text letter by letter until it doesn't overflow anymore. I opted to go with for loops rather than intervals, because even with 1s intervals, you could see the text getting removed letter by letter, and it's much faster with the for loop. If the wrapper has any direct children (maybe a tag with elements), my plugin goes in reverse order to snip text from them until there is no overflow.

jQuery's .each slower on Safari than Chrome/Firefox

I have a large HTML table (1,000-1,500 rows, 40 cols wide). I have a few input and select boxes so users can filter rows. The relevant javascript/jquery (note: not entire code base is pasted in as it is not the bottleneck) attached to it looks like:
function autoRank() {
// auto number
rank = 0;
$("#myTablePlayers .playerData").each(function() {
if ($(this).css("display") != "none") {
rank++;
$(this).find('td').eq(colRank).text(rank);
}
});
}
var teamCols = $(),
GPCols = $(),
posCols = $(),
ageCols = $();
$("#myTablePlayers .playerData").each(function() {
var columns = $(this).find('td');
teamCols = teamCols.add($(".colTeam", this));
GPCols = GPCols.add(columns.eq(colGP));
posCols = posCols.add(columns.eq(colPos));
ageCols = ageCols.add(columns.eq(colAge))
});
function filterTable() {
// Need some error checking on input not number
minGP = $("#mingp").val()
teams = $("#teamFilter").val().toUpperCase()
position = $("#position").val()
age = $("#age").val()
$("#myTablePlayers .playerData").show();
/* Loop through to check for teams */
if (teams) {
teamCols.each(function() {
if (!this.innerHTML.toUpperCase().includes(teams)) {
$(this).parent().hide();
}
});
}
/* Loop and check for min GP */
GPCols.each(function() {
if ( Number(this.innerHTML) < minGP) {
$(this).parent().hide();
}
});
/* Check for age requirement */
if (age) {
age = Number(age)
ageCols.each(function() {
thisAge = Number(this.innerHTML);
if ( thisAge < age || thisAge >= age+1 ) {
$(this).parent().hide();
}
});
}
/* Check the position requirement */
if (position) {
posCols.each(function() {
var thisPos = this.innerHTML
if (position == "D") {
if (thisPos.indexOf("D") == -1) {
$(this).parent().hide();
}
} else if (position == "F") {
if (thisPos.indexOf("D") != -1) {
$(this).parent().hide();
}
} else if (thisPos != position) {
$(this).parent().hide();
}
});
}
autoRank();
}
When stripping down the code as minimal as possible, the offending code is the
var.each(function() { ...
in the filterTable() function.
When I run this on Chrome or Firefox it runs quickly (sub 1 second) and the DOM is rendered properly. When I execute on Safari it takes 30+ seconds.
Why is this and what can I do to adapt for this browser?
jQuery: 1.11.1 (same issue even after upgrading to 3.1.1).
Safari: 10.0.1
Firefox: 50
Chrome: 54.0.
After removing all the repetition and unnecessary complication from your code, this is what remains:
var colRank = 0, colTeam = 1, colGP = 2, colAge = 3, colPos = 4;
function filterTable() {
var minGP = +$("#mingp").val();
var age = +$("#age").val();
var teams = $("#teamFilter").val().toUpperCase();
var position = $("#position").val();
var rank = 0;
$("#myTablePlayers .playerData").each(function () {
if (
(teams && this.cells[colTeam].textContent.toUpperCase().includes(teams)) ||
(minGP && +this.cells[colGP].textContent < minGP) ||
(age && (+this.cells[colAge].textContent < age || +this.cells[colAge].textContent >= age+1)) ||
((position === "D" || position === "F") && this.cells[colPos].textContent.indexOf(position) === -1) ||
(!(position === "D" || position === "F") && (this.cells[colPos].textContent !== position))
) {
this.cells[colRank].textContent = ++rank;
this.style.display = "";
} else {
this.style.display = "none";
}
});
}
I've already removed almost all jQuery in favor of native DOM manipulation.
The remaining .each() could be tuned into a plain old for loop over the document.getElementById('myTablePlayers').tBodies[0].rows, if you want to squeeze out the last bit of possible performance.
Reorder the if conditions by likeliness: From the one that will typically filter out the most rows to the one that will filter out the least rows. Because JS short-circuits conditions, this way less conditions are checked overall.
Making the table display: fixed can also improve render performance at the expense of flexibility.
Finally, you can use CSS to do counters. This is probably faster than manually setting the contents of a table cell. Test for yourself.

Categories

Resources