I am making a page that uses buttons to display the content when the creen is wider than 500px, and then it moves to an 'accordion' style layout when the screen is smaller than 500px.
I am using event listeners and match media along with an if/else statement to determine which code to run.
However, when the code is run with a window of size less than 500px, then dragged out to make it bigger than 500px, the buttons are not responding to the 'onclick' when pressed and nothing happens.
Could somebody please advise where I am going wrong here?
Please see JSfiddle here
//Start listing variables for use in array.
var art1 = document.getElementById("article1");
var button1 = document.getElementById("list1");
var art2 = document.getElementById("article2");
var button2 = document.getElementById("list2");
var art3 = document.getElementById("article3");
var button3 = document.getElementById("list3");
var articleArray = [art1, art2, art3];
var buttonArray = [button1, button2, button3];
function mediaQuery(x) {
//If window is under 500px in width.
if (x.matches) {
//Begin accordion code
var initAccordion = function(accordionElement) {
function handlePanelClick(event) {
showPanel(event.currentTarget);
}
function showPanel(panel) {
var expandedPanel = accordionElement.querySelector(".active");
if (expandedPanel) {
expandedPanel.classList.remove("active");
}
panel.classList.add("active");
}
var allPanelElements = accordionElement.querySelectorAll(".panel");
for (var y = 0; len = allPanelElements.length; y++ ) {
allPanelElements[y].addEventListener("click", handlePanelClick);
}
showPanel(allPanelElements[0]);
}
initAccordion(document.getElementById("contentWrapper"));
}
else //If window if is over 500px in width.
{
//Begin button code.
var createfunc = function (i) {
return function() { document.getElementById("fillMe").innerHTML = articleArray[i].innerHTML;};
}
for (let i=0; i < articleArray.length; i++) {
let button = buttonArray[i];
button.addEventListener("click", createfunc(i));
}
}
}
//Declare media query and add listener.
var x = window.matchMedia("(max-width: 500px)")
mediaQuery(x) // Call listener function at run time
x.addListener(mediaQuery) // Attach listener function on state changes
Related
I'm making a simple program of an etch-a-sketch. I have 2 buttons. 1 that resets the screen, the other makes a new screen and lets you pick the number of pixels in the draw area. The default size works, and the reset works. When I click the new button and set the number of pixels in the draw area updates but the eventlistener stops working and the mouse over won't change the the background color of the divs anymore. Here is my code:
const screen = document.querySelector('.screen')
const clearButton = document.querySelector('.clear-btn');
const newButton = document.querySelector('.new-btn');
var size = 64;
function createGrid(size) {
document.styleSheets[0].cssRules[3].style["grid-template-columns"] = "repeat(" + size + ", 1fr)"
document.styleSheets[0].cssRules[3].style["grid-template-rows"] = "repeat(" + size + ", 1fr)"
console.log('createGrid');
for (i = 0; i < size*size; i++) {
const grid = document.createElement('div');
grid.classList.add('grid');
grid.style.cssText = 'color: #cccccc; background: #cccccc; border: solid 1px lightgrey;';
screen.appendChild(grid);
}
}
function reset() {
for (i = 0; i < size*size; i++) {
grid[i].style.backgroundColor = "#cccccc";
}
}
function newSize(){
let userResponse = prompt("Please enter size of canvas: ", "");
size = parseInt(userResponse);
remove();
createGrid(size);
}
function remove(){
while (screen.firstChild) {
console.log(size);
screen.removeChild(screen.firstChild);
}
}
createGrid(size);
clearButton.addEventListener('click', reset);
newButton.addEventListener('click', newSize);
var grid = document.getElementsByClassName('grid');
Array.from(grid).forEach((tile) => {
tile.addEventListener('mouseover', (e) => {
e.target.style.background = '#0d0d0d';
});
})
You need to add your event listener when you are creating the grid. You remove your grid, which the event original event listener has been attached and the newly created one doesn't have anything attached to it:
function createGrid(size) {
document.styleSheets[0].cssRules[3].style["grid-template-columns"] = "repeat(" + size + ", 1fr)"
document.styleSheets[0].cssRules[3].style["grid-template-rows"] = "repeat(" + size + ", 1fr)"
console.log('createGrid');
for (i = 0; i < size*size; i++) {
const grid = document.createElement('div');
grid.classList.add('grid');
grid.style.cssText = 'color: #cccccc; background: #cccccc; border: solid 1px lightgrey;';
screen.appendChild(grid);
// Add listener to grid cell
grid.addEventListener('mouseover', (e) => {
e.target.style.background = '#0d0d0d';
})
}
}
You might also have a look at event delegation so that you don't have to add a listener per cell and can add it on the container.
The reason for this is that you now have new divs. Your listener was attached to the original HTMLNodes, and now you added new ones, they do not have any listeners attached. The solution would be to:
- clean up the old listeners (in remove function)
- attach the new listeners (in newSize function)
Move the last part (from var grid = document.getElementsByClassName...) to a function, and call it at the end of new size function, something like this:
function atachListeners() {
const grid = document.getElementsByClassName('grid');
Array.from(grid).forEach(tile => {
tile.addEventListener('mouseover', ...);
});
};
Now, your newSize function is like this:
function newSize(){
let userResponse = prompt("Please enter size of canvas: ", "");
size = parseInt(userResponse);
remove();
createGrid(size);
attachListeners();
}
And the remove gets the addition:
function remove() {
removeTileListeners();
while (screen.firstChild) {
screen.removeChild(screen.firstChild);
}
}
Implementing the removeListeners() method, I leave to you as homework :)
Wonder if anyone could give me some guidance. I'm trying to reposition a div based on:
if fpd-actions-wrapper has a class of fpd-pos-outside then position in body.
else position (as below) in main wrapper.
//set action button to specific position
var _setActionButtons = function(pos) {
fpdInstance.$mainWrapper.append('<div class="fpd-actions-wrapper fpd-pos-'+pos+'"></div>');
var posActions = instance.currentActions[pos];
for(var i=0; i < posActions.length; ++i) {
var actionName = posActions[i],
$action = $actions.children('[data-action="'+actionName+'"]');
fpdInstance.$mainWrapper.children('.fpd-actions-wrapper.fpd-pos-'+pos).append($action.clone());
}
};
I've tried below and it does get .fpd-actions-wrapper out of the main wrapper but it's empty - can't work out the last line.
//set action button to specific position
var _setActionButtons = function(pos) {
$body = $('body');
$body.append('<div class="fpd-actions-wrapper fpd-pos-'+pos+'"></div>');
var posActions = instance.currentActions[pos];
for(var i=0; i < posActions.length; ++i) {
var actionName = posActions[i],
$action = $actions.children('[data-action="'+actionName+'"]');
fpdInstance.$mainWrapper.children('.fpd-actions-wrapper.fpd-pos-
'+pos).append($action.clone());
}
};
You need to add position:aboslute; top:10px; left:10px to you div tag. Only divisions with fixed or absolute positions can be moved pro-grammatically. You must define x (distance to left side) and y (distance to top of screen) motion independently. I only defined the 'top' position. You need to add a 'left' point of reference as well. It may not be a simple 'left:'+pos+'px;', so I did not add that code for you except in the div tag.Notice that the actual commands to change position are very much 'css' like. The div tag uses css syntax, so must your code.
//set action button to specific position
var _setActionButtons = function(pos) {
fpdInstance.$mainWrapper.append('<div class="fpd-actions-wrapper fpd-pos-'top:'+pos+'px; fpd-pos-'left:'+pos+'px;"></div>');
var posActions = instance.currentActions['top':+pos+"px;"];
for(var i=0; i < posActions.length; ++i) {
var actionName = posActions[i],
$action = $actions.children('[data-action="'+actionName+'"]');
fpdInstance.$mainWrapper.children('.fpd-actions-wrapper.fpd-pos-':'+pos'px;').append($action.clone());
}
};
I found this code on Stack Overflow: change images on click.
And it works for me. I want it to be random, but how can i prevent it from repeating the images. It should first repeat the images, when the user has clicked through all images.
I have made an JSfiddle with my code: http://jsfiddle.net/gr3f4hp1/
JQuery code:
var images = ["02.jpg","03.jpg","01.jpg"];
$(function() {
$('.change').click(function(e) {
var image = images[Math.floor(Math.random()*images.length)];
$('#bg').parent().fadeOut(200, function() {
$('#bg').attr('src', 'items/'+image);
$(this).fadeIn(200);
});
});
});
Keep track of the numbers that you have generated, and if its a repeat then get a new number.
You can Work Around This
var usedImages = {};
var usedImagesCount = 0;
function displayImage(){
var num = Math.floor(Math.random() * (imagesArray.length));
if (!usedImages[num]){
document.canvas.src = imagesArray[num];
usedImages[num] = true;
usedImagesCount++;
if (usedImagesCount === imagesArray.length){
usedImagesCount = 0;
usedImages = {};
}
} else {
displayImage();
}
}
you can try this code .
var images = ["02.jpg","03.jpg","01.jpg"];
var imagecon = [];
$(function() {
$('.change').click(function(e) {
if(images.length == 0) { // if no image left
images = imagecon; // put all used image back
imagecon = []; // empty the container
}
var index = Math.floor(Math.random()*images.length);
var image = images[index];
imagecon.push(image); // add used image
images.splice(index,1); // remove it to images
$('#bg').parent().fadeOut(200, function() {
$('#bg').attr('src', 'items/'+image);
$(this).fadeIn(200);
});
});
});
you could add a class to every image that appears like:
image.addClass("viewed")
and write an if statement that show the image only if it hasn't the class viewed:
if(!image.hasClass(viewed)){
(show the image methods);
image.addClass("viewed")
}
I'm not going to make all the code for you, just try this way, learn, fail, success, have fun! :D
Try this image change according to imgCnt variable.
var images = ["01.jpg","02.jpg","03.jpg","01.jpg"];
var imgCnt=1;
$(function() {
$('.change').click(function(e) {
// var image = images[Math.floor(Math.random()*images.length)];
if(imgCnt >= images.length){
imgCnt=0;
}
var image =images[imgCnt];
// alert(image);
$('#bg').parent().fadeOut(200, function() {
$('#bg').attr('src', 'items/'+image);
$(this).fadeIn(200);
imgCnt++;
});
});
});
Demo
There are lots of method here i just push the value of visited image in to Visited array
Campate two array show only differ element
var images = ["02.jpg","03.jpg","01.jpg"];
var visited = [];
$(function() {
$('.change').click(function(e) {
var image = images[Math.floor(Math.random()*images.length)];
visited.push(image);
$('#bg').parent().fadeOut(200, function() {
y = jQuery.grep(images, function(value) {
if(jQuery.inArray(value, visited) == -1){
$('#bg').attr('src', 'items/'+image);
$(this).fadeIn(200);
}
});
} });
});
});
I hopes its help
I'm trying to create multiple instances of a slider on a page. Each slider should know which slide it’s currently viewing. It seems that when I update the slide property, I change it for the class, and not the instance. This suggests to me that I'm not instantiating properly in my public init() function. Where am I going wrong?
var MySlider = (function() {
'use strict';
var animating = 0,
slides = 0, // total slides
slider = null,
slide = 0, // current slide
left = 0;
function slideNext(e) {
if ((slide === slides - 1) || animating) return;
var slider = e.target.parentNode.children[0],
x = parseFloat(slider.style.left);
animate(slider, "left", "px", x, x - 960, 800);
slide++;
}
return {
init: function() {
var sliders = document.querySelectorAll('.my-slider'),
l = sliders.length;
for (var i = 0; i < l; i++) {
sliders[i] = MySlider; // I don't think this is correct.
slider = sliders[i];
buildSlider(slider);
}
}
}
})();
Based on your comment, I think this is more like what you want:
MySlider = (function () {
Slider = function (e) {
this.e = e;
// other per element/per slider specific stuff
}
...
var sliders; // define this out here so we know its local to the module not init
return {
init: function () {
sliders = document.querySelectorAll('.my-slider');
var l = sliders.length;
for (var i = 0; i < l; i++) {
sliders[i] = new Slider(sliders[i]); //except I'd use a different array
slider = sliders[i];
buildSlider(slider);
}
}
})();
This way, you are associating each element with it's own element specific data but you have a containing module within which you can operate on your collection of modules.
It seems that when I update the slide property, I change it for the class, and not the instance.
You are correct. That code is run only when the MySlider class is defined. If you want an instance variable, you need to declare it inside the returned object, ie, part of your return block:
var MySlider = (function(param) {
return {
slider: param,
init: function() {
var sliders = document.querySelectorAll('.my-slider'),
l = sliders.length;
for (var i = 0; i < l; i++) {
sliders[i] = MySlider; // I don't think this is correct.
slider = sliders[i];
buildSlider(slider);
}
}
});
How can I make the snow clear after a certain time. I've tried using variables and the calling a timeout which switches on to false and stops the makesnow() function but that doesn't seem to clear the page at all.
<script language="javascript">
ns6 = document.getElementById;
ns = document.layers;
ie = document.all;
/*******************[AccessCSS]***********************************/
function accessCSS(layerID) { //
if(ns6){ return document.getElementById(layerID).style;} //
else if(ie){ return document.all[layerID].style; } //
else if(ns){ return document.layers[layerID]; } //
}/***********************************************************/
/**************************[move Layer]*************************************/
function move(layer,x,y) { accessCSS(layer).left=x; accessCSS(layer).top = y; }
function browserBredde() {
if (window.innerWidth) return window.innerWidth;
else if (document.body.clientWidth) return document.body.clientWidth;
else return 1024;
}
function browserHoyde() {
if (window.innerHeight) return window.innerHeight;
else if (document.body.clientHeight) return document.body.clientHeight;
else return 800;
}
function makeDiv(objName,parentDiv,w,h,content,x,y,overfl,positionType)
{
// positionType could be 'absolute' or 'relative'
if (parentDiv==null) parentDiv='body';
var oDiv = document.createElement ("DIV");
oDiv.id = objName;
if (w) oDiv.style.width = w;
if (h) oDiv.style.height= h;
if (content) oDiv.innerHTML=content;
if (positionType==null) positionType="absolute";
oDiv.style.position = positionType;
if (x) oDiv.style.left=x; else oDiv.style.left=-2000;
if (y) oDiv.style.top=y; else oDiv.style.top=-2000;
if (overfl) oDiv.style.overflow=overfl; else oDiv.style.overflow="hidden";
eval(' document.'+parentDiv+'.appendChild (oDiv); ');
delete oDiv;
}
var snowC=0;
var x = new Array();
var y = new Array();
var speed = new Array();
var t=0;
var cC = new Array();
var ra = new Array();
function makeSnow() {
x[snowC] = Math.round(Math.random()*(browserBredde()-60));
y[snowC] = 10;
makeDiv("snow"+snowC,"body",32,32,'<img src="http://i693.photobucket.com/albums/vv296/KIBBLESGRAMMY/CAT/Orange-tabby-cat-icon.gif">');
speed[snowC] = Math.round(Math.random()*8)+1;
cC[snowC]=Math.random()*10;
ra[snowC] = Math.random()*7;
snowC++;
}
function moveSnow() {
var r = Math.round(Math.random()*100);
if (r>70 && snowC<20) makeSnow();
for (t=0;t<snowC;t++) {
y[t]+=speed[t];move("snow"+t,x[t],y[t]);
if (y[t]>browserHoyde()-50) {y[t] = 10;x[t] = Math.round(Math.random()*(browserBredde()-60));}
cC[t]+=0.01;
x[t]+=Math.cos(cC[t]*ra[t]);
}
setTimeout('moveSnow()',20);
}
moveSnow();
</script>
makeSnow just adds the snowflakes. Stopping that, as you say, does not clear anything. moveSnow handles the animation, and calls itself at a timeout. If instead of setting a timeout for the next moveSnow each time, you set it up to run in an interval just once, you would have an easier time stopping it.
window.snowAnimation = window.setInterval(moveSnow, 20);
If you add a css class to your snow flakes, it would be easier to target them for deletion.
oDiv.className = 'snowflake';
Then your clear function could look something like:
function clearSnow() {
window.clearTimeout(window.snowAnimation);
var flakes = document.getElementsByTagName('snowflake');
for(var i = 0, l = flakes.length; i < l; i++) {
document.body.removeChild(flakes[i]);
}
}
Timeout doesnt help, it helps you only to stop creating new snowdivs, however if you see makeDiv is the one which creates new divs on to the body, if you clear / display:none the divs which got created on makeDiv i hope it will clear all the divs on the screen.
You need to remove the divs that were created. It might be easier if you give them all some sort of class, like ".snowflake" as you create them (in makeDiv), then start removing them from the dom.
You will have to clear the elements created after the time you wanna stop snow falling.
Following code snippet will help you to clear the elements
if(count < 500){
setTimeout('moveSnow()',20);
}else{
var i = 0;
var elem = document.getElementById('snow'+i);
do{
elem.parentNode.removeChild(elem);
elem = document.getElementById('snow'+ ++i);
}while(elem != null)
}
count++;
you have to create a global variable count.