Javascript/Jquery toggle though objects with event - javascript

I have an aframe-scene with two arrows and some objects and i try to toggle through the objects, but i dont get it right some help would be nice here what i got:
AFRAME.registerComponent("fool", {
init: function() {
var boxarray = ["#bluebox, #yellowbox, #greenbox]
boxarray[0] = true;
if(boxarray[0] = true){
document.getElementById('bluebox').setAttribute('visible', 'true');
document.getElementById('bluebox').setAttribute('scale', {x:1,y:1,z:1});
} else {
document.getElementById('bluebox').setAttribute('visible', 'false');
document.getElementById('bluebox').setAttribute('scale', {x:0,y:0,z:0});
}
if(boxarray[1] = true){
document.getElementById('bluebox').setAttribute('visible', 'true');
document.getElementById('bluebox').setAttribute('scale', {x:1,y:1,z:1});
} else {
document.getElementById('bluebox').setAttribute('visible', 'false');
document.getElementById('bluebox').setAttribute('scale', {x:0,y:0,z:0});
}
if(boxarray[2] = true){
document.getElementById('bluebox').setAttribute('visible', 'true');
document.getElementById('bluebox').setAttribute('scale', {x:1,y:1,z:1});
} else {
document.getElementById('bluebox').setAttribute('visible', 'false');
document.getElementById('bluebox').setAttribute('scale', {x:0,y:0,z:0});
}
function toggleright(){
?help?
}
function toggleleft(){
?help?
}
})
I try to give all of my objects(boxes) an event so changing source is just useful if the event changes too

How about having one method which will toggle the "next" entity visible, and the "previous" one invisible.
This way, your left / right method will only determine which one should be next and use the toggling function.
It could be done with a component like this:
AFRAME.registerComponent("foo", {
init: function() {
this.objects = ["one", "two", "three"]
this.iterator = 0
this.left = AFRAME.utils.bind(this.left, this);
this.right = AFRAME.utils.bind(this.right, this);
},
right: function() {
let i = (this.iterator - 1) < 0 ? this.objects.length - 1 : this.iterator - 1
this.toggle(this.iterator, i)
},
left: function() {
let i = (this.iterator + 1) >= this.objects.length ? 0 : this.iterator + 1
this.toggle(this.iterator, i)
},
toggle: function(oldEl, newEl) {
document.getElementById(this.objects[oldEl]).setAttribute("visible", "false")
document.getElementById(this.objects[newEl]).setAttribute("visible", "true")
this.iterator = newEl
}
})
The right and left methods only check if there we didn't reach the beginning / end of the array, and call the toggle method which switches the visibility.
Live fiddle here.

Related

Disable audio play button for defined time even when page is reloaded (with localStorage?)

In this fiddle I made a script building a play button for audio files, that allows playing the file only twice and then disables it. But after reloading the page, the button is active again. How can I make sure, the button stays disabled for a defined duration of 30 seconds for example?
I was wrapping my head around localStorage and found a promising question/answer here, but couldn't transfer the knowhow to my usecase. Can someone help me out?
function buildLimitedPlay(selector) {
$(selector).each(function(i) {
var myaudio = $(this)[0];
var button = $(this).next("input.limited-play")[0];
var index = 2;
$(button).css('display', 'block');
$(button).val("Play Clip");
$(myaudio).on('ended', function() {
index--;
$(button).val('Play again');
if (index == 0) {
$(button).css('background', 'red');
$(button).css('cursor', 'not-allowed');
$(button).css('text-decoration', 'line-through');
$(button).disabled;
}
});
$(button).on("click", function() {
if (index > 0) {
myaudio.play();
}
});
});
}
buildLimitedPlay("audio.limited-play");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<audio class="limited-play" preload="none">
<source src="http://www.noiseaddicts.com/samples_1w72b820/3726.mp3" type="audio/mpeg">
</audio>
<input value="Play Clip" class="limited-play" type="button">
I'm assuming you want to automatically enable the play button after the predefined disabled time passes, also you will have multiple audios to play with multiple buttons. here is the code that will work after refresh and automatically enable the button after time out.
$(function () {
function now() { return +new Date }
var db = window.db = {
get: function (key) {
var entry = JSON.parse(localStorage.getItem(key) || "0");
if (!entry) return null;
if (entry.ttl && entry.ttl + entry.now < now()) {
localStorage.removeItem(key);
return null;
}
return entry.value;
},
set: function (key, value, ttl) {
localStorage.setItem(key, JSON.stringify({
ttl: ttl || 0,
now: now(),
value: value
}));
}
};
function buildLimitedPlay(selector) {
$(selector).each(function (i) {
var getRemainingTurn = function () {
var turn = db.get('remainingTurn' + i);
return null == turn ? 2 : turn;
};
var setRemainingTurn = function (turn, timeToLiveInMillisecond) {
db.set('remainingTurn' + i, turn, timeToLiveInMillisecond || 0);
};
var myaudio = this;
var $button = $(this).next("input.limited-play:first");
$button.css('display', 'block')
.on("click", function () {
if (getRemainingTurn() > 0) {
myaudio.play();
}
});
var setAudioState = function (turn) {
$button.val(2 == turn ? 'Play Clip' : 'Play again');
if (turn == 0) {
$button.css({ 'background': 'red', 'cursor': 'not-allowed', 'text-decoration': 'line-through' });
}
else {
$button.css({ 'background': '', 'cursor': '', 'text-decoration': 'none' });
}
};
var disabledPeriodInMillisecond = 30 * 1000;
var tryEnableAudio = function () {
turn = getRemainingTurn();
if (0 == turn) {
//because we don't know how much time passed since it was disabled in case of a page refresh for simplicity.
setTimeout(tryEnableAudio, 50);
return;
}
setAudioState(turn);
};
$(myaudio).on('ended', function () {
var turn = getRemainingTurn();
turn--;
setAudioState(turn);
if (0 == turn) {
setRemainingTurn(turn, disabledPeriodInMillisecond);
tryEnableAudio();
}
else {
setRemainingTurn(turn);
}
});
setAudioState(getRemainingTurn());
tryEnableAudio();
});
}
buildLimitedPlay("audio.limited-play");
});

possible to alter my tooltip plugin to accept "click" option without too much trouble?

I have a plugin I've written for the purpose of showing/hiding tooltips. It works to the point that I'm happy enough to use it in production work, in the case when all I need is for it to show/hide the tooltip on hover. However I'd like to now alter it to show/hide on click as well.
I can get it to work up to a point but depending on what I try it either doesn't do one of the following: when I click from one tooltip activating element to another the previous tooltip doesn't hide - or - when I click from one element to another the previous tooltip closes but the next tooltip doesn't immediately open as well.
I've tried e.target !== e.currentTarget within an if statement and a number of other things that didn't quite work. My suspicion is that the plugin as it stands is not going to be easily extensible without more than a few simple conditionals, but if anyone can have a look and let me know if possible and if so hopefully offer me some direction or suggestions on how I might go about amending it it would be much appreciated.
If anyone is wondering why I am trying to reinvent the wheel (when there are so many tooltip plugins out there): it's mainly a learning exercise. I'd love to get better at writing reusable code. I'd also like a viable tooltip plugin that suits my needs in regards to being something very lightweight and framework independent, which in my searching I wasn't able to find.
Below is the code as it stands, in one of the two before mentioned states:
(window => {
let template = document.createElement('div'),
pos = {x: 0, y: 0},
targetPos,
tip,
tipWidth,
tipHeight,
innerOffsetX,
delay;
template.inner = document.createElement('div');
template.inner.setAttribute('class', 'tooltip-inner');
template.appendChild(template.inner);
/**
* #param {string|HTMLElement} container Either a selector string or the element.
* #param {Object=} config Optional argument for overriding the default configuration.
*/
class Tooltip {
constructor(container, config) {
this.offsetX = 0;
this.offsetY = 0;
this.position = 'top';
this.margin = 6;
this.offsetBubble = 0;
this.delayShow = this.delayHide = 0;
this.clickToShow = false;
this.tooltipInClass = 'tooltip-in';
this.tooltip = null;
this.toggle = true;
if (typeof container === 'string') {
this.container = document.querySelector(container);
} else {
this.container = container;
}
if (config) {
for (let p in config) {
if (typeof config[p] === 'object') {
this.delayShow = config.delay.show;
this.delayHide = config.delay.hide;
} else {
this[p] = config[p];
}
}
}
template.setAttribute('class', 'tooltip ' + this.position);
}
show() {
return e => {
if (e.target.hasAttribute('data-tooltip')) {
if (this.toggle === true) {
targetPos = e.target.getBoundingClientRect();
pos.x = targetPos.left + e.target.offsetWidth / 2 + this.offsetX;
pos.y = targetPos[this.position] + this.offsetY + document.body.scrollTop;
template.inner.innerText = e.target.getAttribute('data-tooltip');
this.tooltip = document.body.appendChild(template.cloneNode(true));
tip = this.tooltip;
tipWidth = tip.clientWidth;
tipHeight = tip.clientHeight;
pos.x -= tipWidth / 2;
pos.y -= this.position === 'bottom' ? -8 : tipHeight;
// Nudge tooltip content into the window area if needed
if (pos.x + tipWidth > tip.offsetParent.clientWidth) {
innerOffsetX = pos.x + tipWidth - tip.offsetParent.clientWidth + 6;
tip.firstChild.setAttribute('style', `left:-${innerOffsetX}px`);
} else if (pos.x < 0) {
innerOffsetX = -pos.x + 6;
tip.firstChild.setAttribute('style', `left:${innerOffsetX}px`);
}
// Reposition tooltip below/above target and flip arrow if needed
if (pos.y < 0 && this.position !== 'bottom') {
pos.y += tipHeight * 2;
tip.classList.remove(this.position);
tip.classList.add('bottom');
} else if (pos.y + tipHeight > tip.offsetParent.scrollHeight && self.position !== 'top') {
tip.classList.remove(this.position);
tip.classList.add('top');
}
tip.setAttribute('style', `left:${Math.floor(pos.x)}px;top:${Math.floor(pos.y)}px`);
if (this.delayShow !== 0) {
// Don't delay showing the tooltip if entering an adjacent item with a "tooltip" data attribute.
if (e.relatedTarget.hasAttribute('data-tooltip')) {
delay = 0;
} else {
delay = this.delayShow;
}
if (typeof this.timeoutid === 'number') {
clearTimeout(this.timeoutid);
delete this.timeoutid;
}
this.timeoutid = setTimeout(function () {
tip.classList.add(this.tooltipInClass);
this.toggle = false;
}, delay);
} else {
tip.classList.add(this.tooltipInClass);
this.toggle = false;
}
} else {
this.hide('hide');
this.toggle = true;
}
}
};
}
hide(e) {
if (e.target && e.target.hasAttribute('data-tooltip') || typeof e === 'string') {
document.body.lastChild.classList.remove(this.tooltipInClass);
document.body.removeChild(document.body.lastChild);
}
}
init() {
if (!this.clickToShow) {
this.container.addEventListener('mouseover', this.show());
this.container.addEventListener('mouseout', this.hide);
} else {
this.container.addEventListener('click', this.show());
}
}
}
window.Tooltip = window.Tooltip || Tooltip;
})(window);
// Usage examples
const elem = document.querySelector('.container'),
tooltip = new Tooltip(elem, {
offsetX: -2,
delay: {show: 0, hide: 0},
clickToShow: true
}).init();
And a jsfiddle: http://jsfiddle.net/damo_s/et9hLnkt/
Again, any help would be much appreciated.
I changed a little your plugin, I added two thing to handle the problem:
I added this if to control if the link you clicked is the same target if some tooltip is already opened:
if (document.getElementsByClassName("tooltip-in")[0] &&
e.target !== document.getElementsByClassName("current-target-tooltip")[0]) {
this.hide('hide');
this.toggle = true;
document.getElementsByClassName("current-target-tooltip")[0].classList.remove("current-target-tooltip");
}
then I just added the add/remove class on the current target in if/else:
if (this.toggle === true) {
e.target.classList.add('current-target-tooltip');
...
} else {
e.target.classList.remove('current-target-tooltip');
this.hide('hide');
this.toggle = true;
}
and here is your updated Fiddle: http://jsfiddle.net/et9hLnkt/18/

JS Variable Scope and class change

I have this small little app that I built but I was wondering if there was a way to make this code more condensed/easier
var i = -1;
$( "#next, #back, #remove" ).click(function () {
var filters = [ "aden", "reyes", "perpetua", "inkwell",
"toaster", "walden", "hudson",
"gingham", "mayfair", "lofi",
"xpro2", "1977", "brooklyn"]
function changefilter(){
$('figure').removeClass().addClass(filters[i]);
$('h1').text(filters[i]);
console.log(filters[i]);
}
if (this.id == 'next' && i < filters.length) {
i++;
changefilter();
} else if (this.id == 'back' && i > -1 && i !== undefined) {
i--;
changefilter();} else if (this.id == 'remove'){
i = -1;
changefilter();
}
});
basically the app has 3 buttons which will cycle the class on the figure element.
I am still really new to javascript and I know this would have to do something with the scope but why is it that if I put the "i" variable at the very top (inside) of my function
var i = -1;
$( "#next, #back, #remove" ).click(function () {
var filters = [ "aden", "reyes", "perpetua", "inkwell",
"toaster", "walden", "hudson",
"gingham", "mayfair", "lofi",
"xpro2", "1977", "brooklyn"]
the counter does not move? Also, i put "i" to be -1 because i didn't want to have a class applied until the button was hit. Is this a good way to do this? I also noticed that when pressing the remove button the title stays at the most recent selection and does not revert back to the -1 option
Is there a way to have a variable that all functions, within a function, are able to see without making it global? Is there even a point to doing that? Am I even making sense anymore?
Thanks everyone!
If you want to see this i have a pen at http://codepen.io/Oreilly617/pen/KdrOom
var i = -1;
$( "#next, #back, #remove" ).click(function () {
var filters = [ "aden", "reyes", "perpetua", "inkwell", "toaster", "walden",
"hudson", "gingham", "mayfair", "lofi", "xpro2", "1977", "brooklyn"];
switch (this.id) {
case 'next':
changefilter(++i);
break;
case 'back':
changefilter(--i);
break;
case 'remove':
i = -1;
$('h1').text('Fun Filter');
break;
}
function changefilter(i){
var length = filters.length;
var figure = $('figure');
var current_class = figure.prop('class');
if (i >= 0 && $.inArray(current_class) < (length - 1)) {
figure.removeClass(current_class);
$('figure').addClass(filters[i])
$('h1').text(filters[i]);
}
}
});
I am not sure this meets your "more condensed" but it does resolve the issue of variable scope and demonstrates creation and use of an object to contain functions etc. Some of this is still a bit verbose but left that way for clarity. Note that this also "cycles" through your list and returns back to the base when the next/back reach the end of the cycle as well.
Here is a fiddle if you wish to play around with it: http://jsfiddle.net/MarkSchultheiss/9vzy3rLm/1/
var mything = {
basei: -1,
i: this.basei,
figureSelector: 'figure.lens',
defaultDisplay: 'Fun Filterc',
filters: ["aden", "reyes", "perpetua", "inkwell",
"toaster", "walden", "hudson",
"gingham", "mayfair", "lofi",
"xpro2", "1977", "brooklyn"],
maxFilter: 0,
maxFilterCalc: function () {
this.maxFilter = this.filters.length - 1;
},
changefilter: function changefilter() {
$(this.figureSelector).removeClass(this.filters.join(' ')).addClass(this.filters[this.i]);
if (this.i == -1) {
$('h1').text(this.defaultDisplay);
} else {
$('h1').text(this.filters[this.i]);
}
},
processButton: function (me) {
this.maxFilterCalc();
if (me.id == 'next') {
if (this.i < this.maxFilter) {
this.i++;
} else {
this.i = this.basei;
}
} else if (me.id == 'back') {
if (this.i > this.basei) {
this.i--;
} else {
this.i = this.maxFilter;
}
} else if (me.id == 'remove') {
this.i = this.basei;
}
this.changefilter();
}
};
$("#next, #back, #remove").click(function () {
mything.processButton(this);
});

Jquery : swap two value and change style

i need to make a script for select a black div by click(go red), and put black div value into a white div value by another click, this is ok but when i try to swap values of two white case, the change do correctly one time, but if i retry to swap two value of white case the values swap correctly but whitout the background color red.
This is my code :
var lastClicked = '';
var lastClicked2 = '';
$(".blackcase").click(function(e) {
var i = 0;
if ($(this).html().length == 0) {
return false;
} else {
e.preventDefault();
$('.blackcase').removeClass('red');
if (lastClicked != this.id) {
$(this).addClass('red');
var currentId = $(this).attr('id');
var currentVal = $(this).html();
$(".whitecase").click(function(e) {
$('.blackcase').removeClass('red');
var currentId2 = $(this).attr('id');
if (i <= 0 && $("#" + currentId2).html().length == 0) {
$("#" + currentId2).html(currentVal);
$("#" + currentId).html("");
i = 1;
}
});
} else {
lastClicked = this.id;
}
}
});
$(".whitecase").click(function(e) {
var j = 0;
if ($(this).html().length == 0) {
return false;
} else {
e.preventDefault();
$('.whitecase').removeClass('red');
if (lastClicked2 != this.id) {
$(this).addClass('red');
var currentId0 = $(this).attr('id');
var currentVal0 = $(this).html();
$(".whitecase").click(function(e) {
e.preventDefault();
var currentId02 = $(this).attr('id');
var currentVal02 = $(this).html();
if (j <= 0 && currentVal0 != currentVal02) {
$('.whitecase').removeClass('red');
$("#" + currentId02).html(currentVal0);
$("#" + currentId0).html(currentVal02);
j = 1;
return false;
}
});
} else {
lastClicked2 = this.id;
}
}
});
This is JSfiddle :
https://jsfiddle.net/12gwq95u/12/
Try to take 12 and put into first white case, put 39 into second white case, click on the white case with 12 (go red) then click on the white case with 39, the values swap correctly with the red color when it's select, but if you try to reswap two whitecase values thats work but without the red color.
Thanks a lot
I have spent some time to rewrite your code to make it more clear. I don't know what exactly your code should do but according to the information you have already provided, my version of your code is the following:
var selectedCase = {color: "", id: ""};
function removeSelectionWithRed() {
$('div').removeClass('red');
}
function selectWithRed(element) {
removeSelectionWithRed();
element.addClass('red');
}
function updateSelectedCase(color, id) {
selectedCase.color = color;
selectedCase.id = id;
}
function moveValueFromTo(elemFrom, elemTo) {
elemTo.html(elemFrom.html());
setValueToElem("", elemFrom);
}
function setValueToElem(value, elem) {
elem.html(value);
}
function swapValuesFromTo(elemFrom, elemTo) {
var fromValue = elemFrom.html();
var toValue = elemTo.html();
setValueToElem(fromValue, elemTo);
setValueToElem(toValue, elemFrom);
}
function isSelected(color) {
return selectedCase.color == color;
}
function clearSelectedCase() {
selectedCase.color = "";
selectedCase.id = "";
}
function elemIsEmpty(elem) {
return elem.html().length == 0;
}
$(".blackcase").click(function (e) {
if (elemIsEmpty($(this))) {
return;
}
alert("black is selected");
selectWithRed($(this));
updateSelectedCase("black", $(this).attr("id"), $(this).html());
});
$(".whitecase").click(function (e) {
removeSelectionWithRed();
if (isSelected("black")) {
alert("moving black to white");
moveValueFromTo($("#"+selectedCase.id), $(this));
clearSelectedCase();
return;
}
if(isSelected("white") && selectedCase.id !== $(this).attr("id")) {
alert("swap whitecase values");
swapValuesFromTo($("#"+selectedCase.id), $(this));
clearSelectedCase();
return;
}
alert("white is selected");
selectWithRed($(this));
updateSelectedCase("white", $(this).attr("id"), $(this).html());
});
Link to jsfiddle: https://jsfiddle.net/12gwq95u/21/
If my answers were helpful, please up them.
It happens because you have multiple $(".whitecase").click() handlers and they don't override each other but instead they all execute in the order in which they were bound.
I advise you to debug your code in browser console by setting breakpoints in every click() event you have (in browser console you can find your file by navigating to the Sources tab and then (index) file in the first folder in fiddle.jshell.net).
In general I think you should rewrite you code in such a way that you won't have multiple handlers to the same events and you can be absolutely sure what your code does.

Callback after $.each with animations

So I'm running some animations to bring in some divs in my first jQuery plugin:
$.fn.turnstile = function (options, callback) {
if (!this.length) {
return this;
}
count = this.length;
var opts = $.extend(true, {}, $.fn.turnstile.defaults, options)
var delayIt = 100;
if (opts.direction == "in") {
opts.deg = '0deg';
opts.trans = '0,0';
opts.opacity = 1;
} else if (opts.direction == "out") {
opts.deg = '-90deg';
opts.trans = "-100px, 200px";
opts.opacity = 0;
} else if (opts.direction == "back") {
opts.deg = '0deg';
opts.trans = '-2000px,0px';
opts.opacity = 0;
} else {
opts.deg = direction;
}
this.each(function (index) {
delayIt += opts.delayer;
$(this).show().delay(delayIt).transition({
perspective: '0px',
rotateY: opts.deg,
opacity: opts.opacity,
translate: opts.trans
}, 400, 'cubic-bezier(0.33,0.66,0.66,1)', function () {
if (opts.direction == "back" || opts.direction == "out") {
$(this).hide();
}
if (!--count && callback && typeof (callback) === "function") {
if ($(":animated").length === 0) {
callback.call(this);
}
}
});
});
return this;
};
Now, I'd like to call my callback when all animations are completed, which should mathematically be (delayIt+400)*count - but I can't get the callback to run when all of the animations are complete. As you might be able to tell from its current state, I've attempted to check for :animated, used the !--count condition, and even tried setting a timeout equal to the duration of the animations, but all seem to fire asynchronously. What is the best way to animate these and call something back when done?
Here's the info on the .transition() plugin.
Not tested with your code (i'm not familiar with .transition), but try this:
...
this.promise().done(function(){
alert('all done');
});
return this;
};

Categories

Resources