I have following code working so far: JSFIDDLE DEMO
The relevant JS is here:
// Define classes & background element.
var classes = ['bg1','bg2','bg3','bg4'],
$bg = document.getElementById('blah');
// On first run:
$bg.className = sessionStorage.getItem('currentClass') || classes[0];
// On button click:
$('.swapper').mousedown(function () {
// (1) Get current class of background element,
// find its index in "classes" Array.
var currentClassIndex = classes.indexOf($bg.className);
// (2) Get new class from list.
var nextClass = classes[(currentClassIndex + 1)%classes.length];
// (3) Assign new class to background element.
$bg.className = nextClass;
// (4) Save new class in sessionStorage.
sessionStorage.setItem('currentClass', nextClass);
});
For my purposes, this functionally working great -- I can click a single button to continually swap between those four classes while also storing the current class to sessionStorage, so that when I click links on my website, the currentClass is loaded right away. (Note: on my website the setup is the same, but the classes bg1, bg2, bg3, and bg4 contain background images.)
What I'd like it to do:
When swapping from one class to another, I'd like it to do a quick/short cross-fade. Right now it just snaps from one class/background to another.
My thinking was: is there a way I can trigger a CSS class transition or animation that contains the fade, perhaps as a parent class? I know there's a jQuery fade function, but I haven't been able to get it working with my setup so that it triggers on mouseClick.
Here's an updated jsfiddle based on your comment where you said you've sort of having it work.
I've added the timeout functions
setTimeout(function(){$bg.className = nextClass}, 500);
setTimeout(function(){$($bg).fadeIn(500)}, 500)
The first timeout makes it so that the image is swapped right after the first image fades out. The second timeout gives it a bit of time to load in so it's not so jittery.
You can play with the }, 500); number to get it timed just like you want, 500 is half a second, 1000 is a second etc.
Related
First of all a disclaimer, I'm not a dev. I'm halfway through The Odin Project and have covered some HTML and CSS, but, have not yet started on JS. In order to help with my learning I've created my own blog. My aim is for each blog post to have its own stylesheet (so with each new post I learn a little more about CSS).
Anyway, I plan to write a post about the benefits of using an eReader, specifically the Kindle. I've styled the page to look like a Kindle Oasis, and I'd like the reader to be able to step through the article contents via the Kindle's next/prev buttons, but, as I'm not a dev, this is where I'm stuck. Via Stack overflow I've managed to add some JS that will display page 1, 2 and 3 via dedicated buttons for each dive element, but, what I really need is to step through x number of pages via the prev/next buttons.
Here's what I have so far: https://codepen.io/dbssticky/pen/yLVoORO. Any help would be much appreciated. What I should do of course is finish The Odin Project and come up with a solution on my own, but, I'd really like to get this Kindle article published sooner rather than later. Hence my rather cheeky request for assistance.
Here's the JS I'm currently using:
function swapContent(id) {
const main = document.getElementById("main_place");
const div = document.getElementById(id);
const clone = div.cloneNode(true);
while (main.firstChild) main.firstChild.remove();
main.appendChild(clone);
}
You have the right idea and it just needs a few adjustments to get the previous/next functionality.
Currently your div IDs are following the format operation1, operation2, and so on. Since you want the previous/next functionality you'll need to change your 'swapping' function, which currently takes the full ID, to use the numeric portion only.
Add a new function which appends the number to 'operation' instead of using the whole thing:
function goToPage(pageNumber){
const main = document.getElementById("main_place");
const div = document.getElementById("operation" + pageNumber);
const clone = div.cloneNode(true);
while (main.firstChild) main.firstChild.remove();
main.appendChild(clone);
}
And then change your Page 1/2/3 buttons to use goToPage(1), goToPage(2) and so on.
Now for the previous/next functionality you'll need a way to track which page you're on, so that you can figure out which page to load.
Add a variable at the top (outside functions)
var currentPage = 0;
Then add a line in your goToPage function to track the page you're on.
currentPage = pageNumber;
Now that you're tracking you can add a previous and next function.
function goNextPage(){
goToPage(currentPage-1);
}
function goPreviousPage(){
goToPage(currentPage+1);
}
Then call it from the previous and next buttons.
<button onClick="goNextPage()" class="next-button"></button>
<button onClick="goPreviousPage()" class="previous-button"></button>
Here's a codepen: https://codepen.io/srirachapen/pen/WNZOXQZ
It's barebones and you may have to handle things like non existent div IDs.
HTML
<button class="next-button" onclick="nextContent()"></button>
<button class="previous-button" onclick="prevContent()"></button>
JS
var pageid = 0;
var maxpage = 3;
function nextContent() {
if(pageid == maxpage) return
pageid++
swapContent(`operation${pageid}`)
}
function prevContent() {
if(pageid == 1) return
pageid--
swapContent(`operation${pageid}`)
}
you can try this to switch between pages. But you may need to edit the "swapContent" method more sensibly.
Track the Current Page
Whatever solution you use to render pages & links (manual hardcoded links & content vs externally-stored & auto-generated), one thing is unavoidable: You need to track the current page!
var currentPage = 0
Then, any time there's a page change event, you update that variable.
With the current page being tracked, you can now perform operations relative to it (e.g. +1 or -1)
I'd suggest making a goToPage(page) function that does high-level paging logic, and keep your swapContent() function specifically for the literal act of swapping div content. In the future, you may find you'd want to use swapContent() for non-page content, like showing a "Welcome" or "Help" screen.
Example:
function goToPage(page) {
// Update `currentPage`
currentPage = page
// ... other logic, like a tracking event or anything else you want you occur when pages change
// Do the actual content swap, which could be your existing swapContent()
swapContent('operation'+page)
}
You'd invoke the function like so:
goToPage(3) // Jump to a specific page
goToPage(currentPage + 1) // Go to the next page
goToPage(currentPage - 1) // Go to the prev page
You can make separate helper functions like "goToNextPage()" if you desire, but for sure you start with a fundamental page-change function first.
I'm working on something where multiple functions will add various Event listeners to an initially hidden div, let's just call it secretBlock. Only one will ever be active at any given point, but all said functions will manipulate it by:
First cloning sercetBlock to ensure no previous listeners are still attached
Then setting the display to flex
HTML:
<div id="secretBlock" hidden>Secret</div>
JavaScript:
function exampleFuction() {
var secretBlock = document.getElementById('secretBlock');
var secretClone = secretBlock.cloneNode(true);
secretBlock.parentNode.replaceChild(secretClone, secretBlock);
secretBlock.style.display = 'flex';
....
}
but the last part, setting the display, is not firing.
I assumed this had something to do with async-ness, but
setTimeout(function(){ secretBlock.style.display = 'flex' }, 999);
also had no effect.
However, one of the functions appends the div inside of another div right after setting the display, causing it to fire properly:
secretBlock.parentNode.replaceChild(secretClone, secretBlock);
secretBlock.style.display = 'flex';
otherDiv.appendChild(secretBlock);
After a bit of testing, I found out it doesn't matter when I set the display (now vs later) or where it is in the code, as long as secretBlock gets appended to another div, the display change will register, otherwise staying hidden.
.......which sorta left me clueless as to what's going on, any insight would thus be much appreciated~~
Was a reference issue.
After .replaceChild() replaces secretBlock, the initial reference:
var secretBlock = document.getElementById('secretBlock')
becomes obsolete as it still points to the old, original element which is not apart of the html document anymore. Thus you need to redirect the reference to the cloned element:
secretBlock.parentNode.replaceChild(secretClone, secretBlock);
secretBlock = document.getElementById('secretBlock');
secretBlock.style.display = 'flex';
Thanks Dr.Molle!
Have been trying to learn some basics of web designing involving some simple HTML, CSS, JS/Jquery and have been coming up to certain obstacles that I haven't been able to find a way to work around.
One of the things I'm trying to implement is rotating a small number of divs. At the lack of some proper manner for it, what I rigged up was to .toggle off one of them while toggling on another div that was hidden from page load.
Not the prettiest thing, but it kind of works though oddly enough it only works twice before for some reason the cycle stops working.
function moveSide(){
var intervalId;
var childCount = 2;
var preLast = childCount + 2;
var newLast = childCount + 3;
intervalId = setInterval(function () {
$(".column:nth-of-type(" + childCount + ")").toggle("slide", function(){
$(".column:nth-of-type(" + preLast + ")").removeClass("last").delay(1, function(){
$(".column:nth-of-type(" + newLast + ")").addClass("last").delay(1).toggle("slide", function(){
childCount++;
preLast = childCount + 2;
newLast = childCount + 3;
//alert(childCount);
});
});
});
},5000);
}
I'm not sure if using nth-of-type is the right choice, but it seemed to be allowing me to pick amongst the divs. childCount is to pick which div is to be the first to be toggled off, pre(vious)Last is to identify what was the last div of those displayed in order to remove a class used for some properties, newLast is to identify the div that will become visible and give it the class to add CSS properties.
The alert cycle runs twice entirely (increasing childCount), but doesn't process a third time.
Any ideas what I'm doing wrong?
i don't now, if it's my not cured cold or the fact, that i just got up a few hours ago, but i don't understand your code.
what i understand, is what you want to achieve - and i suggest another method:
$(".column:gt(0)").hide(); //first of all, hide all columns but the first one
setInterval(function() {
$('.column:first') //select the first column
.toggle("slide") //slide it out
.next().next() //select the 3rd column
.toggleClass("last") //remove class "last"
.next() //select first invisible column
.toggleClass("last") //add class last
.toggle("slide") //slide it in
.end().end().end() //end the chain, to reselect the first element
// since we used .next() three times, we have to end it three times
.appendTo('#column-content'); //move the first element inside the dom to the end
}, 5000);
your interval should be running infinite now - always sliding out the first element, sliding in the next element end appending the first element to the end. therefore, the current element is always the first one...
see the fiddle: http://jsfiddle.net/sv5j85df/2/
Below I have this piece of code which I use to filter products with using a drop-down menu. The content of the #child_cat division changes based on the value attribute of the anchor tag:
$('#brandsort').change(function(){
$('#child_cat a').fadeOut(500);
$('[value="' + $(this).val() + '"]').fadeIn();
if ($('#brandsort option:selected').text() === "") {
$('#child_cat a').fadeIn(500);
}
});
The code will filter out the products that do not match their option value, but it won't play the animation. Right now, it acts more like a delayed .show() / .hide() function than anything. Please enlighten me from any wrongdoing in my code or what I could possibly be doing wrong aside from that.
EDIT:
I know the people on SO would normally like some hands-on help from one of you, but in this case I was specifically only asking for "enlightenment". Just some verbal input of what I could have been doing wrong.
To fulfill your request of providing some HTML, you'll find it here: http://jsfiddle.net/HJPN8/3/
There was a few mistakes in the logic that made this not work. Firstly, the reason you couldn't see the fade animate happen is because fade uses the css property opacity. Opacity only works on block and inline-block elements, and you were using the .fadeOut() on a tags which are display:inline. So that can be fixed easily with this:
#child_cat a{
display:block;
}
Next you're using .fadeOut() and .fadeIn() which both run at the same time meaning that the animations would both collide and not work properly. So you need to use callback functions to correctly time them. Below is the code I have refactored, I've included a lot of comments so you can see how it all works. The fade functions have been replaced with .animate() which is a lower end function that gives you more control which we need in this situation.
One last thing is that you were using the value attribute on your products, this isn't recommended as this property is specific to the options tag. If you wish to create custom attributes then the standard way is to prepend them with "data-" which you can see I've done here: http://jsfiddle.net/HJPN8/6/
$(function(){
var brandsort = $('#brandsort');
var products = $('#child_cat a');
brandsort.on('change', function(e){
var val = brandsort.val();
// If search is blank then select all products to show else filter specific products.
var filteredProducts = (val == '') ? products : products.filter('[data-value="' + val + '"]');
// Hide products and set callback for when the animation has finished.
// If we don't use a callback, the products will animate out and in at the same time, ruining the effect.
products.animate({opacity: 0}, 300).promise().done(function(){
// Now that he products' opacity is 0, we set them to display block to remove them from the flow of the DOM.
products.css({display: 'none'});
// Now Bring the filtered products back and animate them in again.
filteredProducts.css({display: 'block'}).animate({opacity: 1}, 500);
});
});
});
I need a tree menu. But instead of a listview where you expand/collapse i need a dropdown box with the list and when you click on a element i need the box to update (with the first entry being 'Back') so the menu stays in a neat little dialog.
Does this menu have a name? Does anyone know where i can get code to do this?
I can think of several jQuery plugins which would soot your purposes. However, I would recommend jQuery iPod Style Drilldown Menu (Newer Version), which is exactly what it sounds like. The dropdown box updates in place, uses a cool sideways slide animation, and includes a "Back" button (as you desired). Finally, if you don't want any animation, you can try tweaking the plugin's many options. Setting crossSpeed to 0 may work, for example.
Adam is right, jQuery offers an assortment of menu's which you could use. Really though, this is a somewhat trivial problem, the code to write it would take up about 1/10th the space that jQuery's code will. So if possible I would say write it without jQuery.
The most effective method would be to do it JS OOP (Javascript Object-Oriented), but understandably this is a confusing topic.
Basically you just want something like:
function drillDown(){
//Any code that multiple drilldowns
// might need on the same page goes here
//Every instance of a drillDown will
// instantiate a new set of all functions/variables
// which are contained here
//A reference to the parent node the dropdown is placed in
this.parent;
//A reference to the div the dropdown is incased in
this.object;
//Returns a reference to this object so it can be
// stored/referenced from a variable in it's
// superclass
return this;
}
//Prototype Functions
//prototypes are shared by all
// instances so as to not double up code
//this function will build the dropdown
drillDown.prototype.build = function(parent){
//Too lazy to write all this, but build a div and your select box
// Add the select box to the div,
// Add the div to the parent (which is in your document somewhere)
var divEle = document.createElement('div');
var inputBox = document.createElement('input');
//code code code
divEle.appendChild(inputBox);
parent.appendChild(divEle);
}
//this function loads the newest dataset of
drillDown.prototype.loadNewDataSet = function(data){
//first clear out the old list
// remember we have a reference to both the
// 'object' and 'parent' by using
// this.object and this.parent
//load the data, we are going to use the text from
// the select boxes to load each new dataset, woo eval();
// If you didn't know, eval() turns a string into JS code,
// in this case referencing an array somewhere
var dataSet = eval(data);
//then loop through your list adding each new item
for(item in dataSet){
//add item to the list
//change the .onClick() of each one to load the next data set
// a la ->
selectItem.onClick = function(){this.loadNewDataSet(item);};
//if you name your datasets intelligently,
// say a bunch of arrays named for their respective selectors,
// this is mad easy
}
}
//Then you can just build it
var drillDownBox = new drillDown();
drillDownBox.build(document.getElementsByTagName('body')[0]);
drillDownBox.loadNewDataSet("start");
//assuming your first dataset array is named "start",
// it should just go
And by the way, Adam also said it, but wasn't explicit, this is refered to as a drill-down.