I have the following simple demo here: https://tinker.io/8e585/1. I have attached code below.
Initially, the contents of both 'Test 1' & 'Test 2' are closed.
However, when clicked, they open. I would like it, if when one is open and then clicked it closes. So, if open AND clicked = close. Is this possible?
Many thanks for any helpers with this :-)
..
HTML
<div class="grid_4">
<h2 style="margin-bottom:4px">Test 1</h2>
<div class="newboxes2" id="newboxes6">
<p>bla 1</p>
</div>
</div>
<div class="grid_4">
<h2 style="margin-bottom:4px">Test 2</h2>
<div class="newboxes2" id="newboxes7">
<p>bla 2</p>
</div>
</div>
CSS
.newboxes2 {display:none}
jQuery
function slideonlyone(thechosenone) {
$('.newboxes2').each(function(index) {
if ($(this).attr("id") == thechosenone) {
jQuery(this).parent('.grid_4').children().find('img.small').attr('src', '/wp-content/themes/boilerplate/images/image_corner_btn_onstate.png');
$(this).slideDown(200);
}
else {
jQuery(this).parent('.grid_4').children().find('img.small').attr('src', '/wp-content/themes/boilerplate/images/image_corner_btn_offstate.png');
$(this).slideUp(600);
}
});
}
You can simply use a class to do it:
https://tinker.io/8e585/12
function slideonlyone(thechosenone) {
$('.newboxes2').each(function(index) {
if (this.id == thechosenone) {
if($(this).is('.active') )
$(this).removeClass('active').slideUp(600);
else
$(this).addClass('active').slideDown(200);
}
else
$(this).removeClass('active').slideUp(600);
if($(this).is('.active') )
jQuery(this).parent('.grid_4').children().find('img.small').attr('src', '/wp-content/themes/boilerplate/images/image_corner_btn_onstate.png');
else
jQuery(this).parent('.grid_4').children().find('img.small').attr('src', '/wp-content/themes/boilerplate/images/image_corner_btn_offstate.png');
});
}
everything should be a lot easier then you think. you should remove your inline javascript event-handler. and use the jquery-toggle-mechanism:
then your javascript code could become as short as this:
$('.grid_4').bind('click', function () {
$(this).find('.newboxes2').slideToggle(200);
});
see the updated tinker for an example: https://tinker.io/8e585/4
if you want your slideDown to be faster (200) than your slideUp (600), you could lookup the current display property:
var duration, $newboxes2;
$('.grid_4').bind('click', function () {
$newboxes2 = $(this).find('.newboxes2');
duration = $newboxes2.css('display') === 'none' ? 200 : 600;
$(this).find('.newboxes2').slideToggle(duration);
});
tinker thats working here: https://tinker.io/8e585/5
code with your imageswap. this code could even be 1 or 2 lines shorter (the if-else), but i leave it like that, to make it easier to read for you:
var duration, $newboxes2, imgSrc, imgBase = '/wp-content/themes/boilerplate/images/';
$('.grid_4').bind('click', function () {
$newboxes2 = $(this).find('.newboxes2');
if ($newboxes2.css('display') === 'none') {
duration = 200;
imgSrc = imgBase + 'image_corner_btn_onstate.png';
} else {
duration = 600;
imgSrc = imgBase + 'image_corner_btn_offstate.png';
}
$(this).find('img.small').attr('src', imgSrc);
$(this).find('.newboxes2').slideToggle(duration);
});
see tinker: https://tinker.io/8e585/13
Sounds like you want an accordion: http://jqueryui.com/accordion/ alternatively, you could use the Javascript below (remove the inline Javascript you have in your HTML and just use '#'):
(function($) {
$(function() {
var links = $('.grid_4 h2:first-child a');
links.addClass('closed');
links.click(function() {
var $this = $(this);
links.each(function() {
var curLink = $(this);
if(curLink !== $this) {
curLink.parents('.grid_4').find('.newboxes2').slideUp(600, function({curLink.addClass('closed');});
curLink.parents('.grid_4').find('img.small').attr('src', '/wp-content/themes/boilerplate/images/image_corner_btn_offstate.png');
}
});
if($this.hasClass('closed')) {
$this.parents('.grid_4').find('.newboxes2').slideDown(200, function() {$this.removeClass('closed');});
$this.parents('.grid_4').find('img.small').attr('src', '/wp-content/themes/boilerplate/images/image_corner_btn_onstate.png');
} else {
$this.parents('.grid_4').find('.newboxes2').slideUp(600, function() {$this.addClass('closed');});
$this.parents('.grid_4').find('img.small').attr('src', '/wp-content/themes/boilerplate/images/image_corner_btn_offstate.png');
}
});
});
})(jQuery);
Related
Disclaimer: I know my code is pretty bad. I'm not very experienced with JavaScript yet.
So I built a gallery with filtering using JavaScript and the WP Rest API. I finally have it working, but after a few clicks between the gallery sub-categories, the page starts slowing down and eventually crashes. I imagine I'm doing something really inefficient that is killing the page, but I'm not sure what the main culprit is.
Besides the other obvious issues with how I've written the code, what might be causing it and is there a good way to test performance issues like this?
(Here's a link to a working version of this: http://victorysurfaces.x10host.com/gallery/)
Edit: Updated code with fix for extra HTML DOM nodes being added by lightbox code. Didn't fix page crashing issue, unfortunately.
Update: I've noticed that sometimes when I click on a sub-category, it makes more network requests, but sometimes it doesn't. I feel like this might be important.
Update 2: I think it might have something to do with the event listeners I'm adding. Since I'm adding the sub-categories dynamically, I have to add the event listeners after they have been loaded, but the event listeners from the previous run seem to remain, so the number of event listeners just grows and grows. I don't know what to do about that.
<div class="gallery">
<div class="medium-wrapper">
<div class="gallery__filters text-center">
<div class="gallery__main-filters">
<button class="category-filter main-category active" data-category="residential">Residential</button>
<span>|</span>
<button class="category-filter main-category" data-category="commercial">Commercial</button>
</div>
<div class="gallery__category-filters"></div>
</div>
<div class="gallery__images"></div>
</div>
</div>
<script>
/* I'm so sorry for this monstrosity. This was way more complicated than I thought and in the end I just wanted it to work */
jQuery(document).ready(function($) {
$('.main-category').on('click', function() {
$('.main-category').removeClass('active');
$(this).addClass('active');
});
initLightbox();
});
jQuery( function( $ ) {
$.getJSON("/wp-json/wp/v2/gallery-categories", function( data ) {
var currentMainCategory = $('.main-category.active').data('category');
getSubCategories();
var currentSubCategory = '';
document.querySelectorAll('.main-category').forEach( function(trigger) {
trigger.addEventListener('click', function() {
resetCategories($(this).data('category')); }, false);
});
function getSubCategories() {
var categoriesArray = [];
var subCategories = data.map(function(category) {
if( category.acf.gallery_section.trim().toLowerCase() === currentMainCategory) {
var setCategory = "<button class='category-filter sub-category' data-category='" + category.acf.category_title + "'>" + category.acf.category_title + "</button>";
categoriesArray.push(setCategory);
}
});
$('.gallery__category-filters').html(categoriesArray);
getPhotos();
}
function resetCategories(mainCategoryTitle) {
currentMainCategory = '';
currentSubCategory = '';
$('.sub-category').removeClass('active');
$('.gallery__category-filters').empty();
currentMainCategory = mainCategoryTitle;
getSubCategories();
}
function setSubCategory() {
currentSubCategory = document.querySelector('.sub-category.active').dataset.category;
getPhotos();
}
var galleryPhotos;
function getPhotos(photos) {
$('.gallery__images').empty();
var mainCategory = currentMainCategory.trim().toLowerCase();
if( (currentSubCategory !== undefined) && (currentSubCategory !== '' ) ) {
var subCategory = currentSubCategory.trim().toLowerCase();
}
galleryPhotos = data.map(function(category) {
if( category.acf.gallery_section.toLowerCase() === mainCategory ) {
if( subCategory !== '' && subCategory !== undefined) {
var categoryTitle = category.acf.category_title.toLowerCase().trim();
if( categoryTitle === subCategory ) {
var galleryCategory = category.acf.gallery_items;
var categoryPhotos = galleryCategory.map(function(photo) {
var galleryPhoto = "<div class='gallery__item'><a class='lightbox-link' href=''><img class='full-width lightbox-target' src='" + photo.gallery_item_image.sizes.flexible + "' alt='" + photo.gallery_item_image.alt + "'></a></div>";
return galleryPhoto;
});
$('.gallery__images').append(categoryPhotos);
}
} else {
var galleryCategory = category.acf.gallery_items;
var categoryPhotos = galleryCategory.map(function(photo) {
var galleryPhoto = "<div class='gallery__item'><a class='lightbox-link' href=''><img class='full-width lightbox-target' src='" + photo.gallery_item_image.sizes.flexible + "' alt='" + photo.gallery_item_image.alt + "'></a></div>";
return galleryPhoto;
});
$('.gallery__images').append(categoryPhotos);
}
}
});
$('.sub-category').on('click', function() {
$('.sub-category').removeClass('active');
$(this).addClass('active');
setSubCategory();
});
checkOrientation();
handleLightboxUpdate();
}
});
});
function checkOrientation() {
document.querySelectorAll('.lightbox-target').forEach(function(item) {
var image = new Image();
image.src = item.src;
image.onload = function() {
if(image.naturalHeight >= image.naturalWidth) {
item.classList.add('portrait');
}
}
});
}
function initLightbox() {
var $overlay = jQuery('<div id="overlay"></div>');
var $container = jQuery('<div class="lightbox">×</div>');
var $image;
var $imageClone;
jQuery('body').append($overlay);
$overlay.click(function(){
$overlay.hide();
});
$overlay.append($container);
}
function handleLightboxUpdate() {
document.querySelectorAll('.lightbox-link').forEach( function(trigger) {
trigger.addEventListener('click', function() {
event.preventDefault();
jQuery('.lightbox-image').remove();
$image = jQuery(this).find('.lightbox-target');
$imageClone = $image.clone();
if($imageClone.hasClass('portrait')) {
$imageClone.addClass('resize-lightbox');
}
jQuery('#overlay').show();
//add image to overlay
$imageClone.addClass('lightbox-image').appendTo('#overlay .lightbox');
});
});
}
</script>
I'm not looking much into code, but I can tell you what's going on here. Page slows down with each 'subcategory' click, because you add more and more HTML nodes into the page until it's just too much. To be specific you add <div id="overlay">...</div> exponentially with every click.
is there a good way to test performance issues like this?
I suggest opening dev tools and see what's happening there. If adding more html wasn't the case, I'd look into potential problems with recursion or creating too many objects.
I figured it out! My setSubCategory() function was calling getPhotos() which was calling setSubCategory(), and so on and so forth.
Turns out it was a simple never-ending loop. Face-palm.
I have a simple method which loads content and should replace it into an div element:
$("a[data-ajax='true']").on('click', function (event) {
event.preventDefault();
var goToLink = this.href;
animateLink(this);
$('#ajax-target').fadeTo(0.2, 0.5, function () {
lastLink = currentLink;
currentLink = goToLink;
$('#ajax-target').load(goToLink, {}, function () {
$('body').scrollTop(0);
$('#ajax-target').fadeTo('slow', 1);
});
});
});
Now I load per ajax HTML code which contains the following code:
<script>
console.log('Running...')
</script>
First time I click on my link which loads the ajax, I see 'Running...' on the console as expected. Second time I do so, 'Running...' is printed twice on the console. Third time this message I get 4 times on the console, and so on. I can't figure out where the problem is. When I load the HTML code per ajax request and replace it per html() method, same problem. Has someone a idea what's wrong?
Edit
After the answer here I have now the following code which works:
<script>
var currentLink, lastLink;
function animateLink(obj) {
var el = $(obj);
var animate = el.attr('data-animation');
if (animate != undefined) {
animate = animate.toLowerCase();
animate = animate == 'false' ? false : true;
} else {
animate = true;
}
if (animate) {
el.toggle('explode');
}
}
$(document).ready(function () {
$("a[data-ajax='true']").on('click', function (event) {
event.stopImmediatePropagation();
event.preventDefault();
var goToLink = this.href;
animateLink(this);
$('#ajax-target').fadeTo(0.2, 0.5, function () {
lastLink = currentLink;
currentLink = goToLink;
$('#ajax-target').load(goToLink, {}, function () {
$('body').scrollTop(0);
$('#ajax-target').fadeTo('slow', 1);
});
});
});
});
function historyBack() {
$('#ajax-target').fadeTo(0.2, 0.5, function () {
var newLink = lastLink;
lastLink = this.href;
currentLink = newLink;
$.get(newLink, {}, function (response) {
$('body').scrollTop(0);
$('#ajax-target')
.html(response)
.fadeTo('slow', 1);
});
});
}
</script>
This is located on the site directly. This part is not removed when the ajax request is done. The part which is always changed is the following:
<div class="content-wrapper" id="ajax-target">
#Html.Partial("LayoutContentHeader")
<section class="content">
#RenderBody()
</section>
</div>
The loaded page is an MVC page, where the main-frame does not contain any HTML or BODY tag, only the content which needs to get replaced and in addition sometimes scripts (how my script above):
#if (IsAjax)
{
if (isMainLayout)
{
#Html.Partial("LayoutContentHeader")
<section class="content">
#RenderBody()
</section>
}
else
{
#RenderBody()
}
<script>
$(function () {
$('[data-toggle="tooltip"]').tooltip()
})
</script>
}
As you can see, when we are in a ajax request, I only send the important content.
And the code of a pressed link is for example:
<a href="/wilhelmshaven/hse/" data-ajax="true" class="btn btn-block btn-social" style="color: white !important; background-color: rgb(243,156,18)">
<i class="fa fa-th"></i>HSE
</a>
with no link around (which can force bubbling).
Try adding this code
$("a[data-ajax='true']").on('click', function (event) {
event.stopImmediatePropagation();
var goToLink = this.href;
animateLink(this);
$('#ajax-target').fadeTo(0.2, 0.5, function () {
lastLink = currentLink;
currentLink = goToLink;
$('#ajax-target').load(goToLink, {}, function () {
$('body').scrollTop(0);
$('#ajax-target').fadeTo('slow', 1);
});
});
});
Reference : http://api.jquery.com/event.stopimmediatepropagation/
I have a list of divs containing images/videos/galleries etc.
The structure is as follows:
<div class="item image">image content</div>
<div class="item video">video content</div>
<div class="item gallery">gallery content</div>
<div class="item image">image content</div>
<div class="item image">image content</div>
<div class="item video">video content</div>
As you can see, there can be more than one div with the same content type.
What I want to achieve is scan the list of divs with class=item and generate a button for each content type.
This is what I have so far, using jQuery EACH function
$(document).ready(function () {
$(".item").each(function () {
if ($(this).hasClass("image")) {
alert('image found');
};
if ($(this).hasClass("video")) {
alert('video found');
};
});
});
Problem is the alert get executed multiple times, for each div with the class equal to my condition. As I am planning to generate buttons for each content type this current code will add duplicate buttons as more than one div can have a class of video/image.
I have tried using "return false" inside the IF condition but that breaks my whole EACH function, stopping it at the first reference.
You can create a temporary variable that keeps track of which item types you have already traversed
(function() {
var types = {},
type_re = /\b(?:audio|video|quote|link|image|gallery|status|chat)\b/g;
$('.item').each(function() {
var m = this.className.match(type_re);
if (m !== null && !types.hasOwnProperty(m[0])) {
// code to add button
console.log('add button for type ' + m[0]);
types[m[0]] = true;
}
});
}());
Demo
Previous answers
You can create an array first that will contain all the types found in the document:
var types = [],
type_re = /audio|video|quote|link|image|gallery|status|chat/g;
$('.item').each(function() {
var m;
while ((m = type_re.exec(this.className)) !== null) {
if (!$.inArray(types, t[0])) {
types.push(t[0]);
}
}
});
// types is an array with all types found
Alternatively, iterate over all possible types and filter the items based on each type:
var $items = $('.item'),
types = ['audio', 'video', 'quote', 'link', 'image', 'gallery', 'status', 'chat'];
$.each(types, function(_, type) {
var $itemsOfType = $items.filter(function() {
return (' ' + this.className + ' ').indexOf(type) != -1;
});
if ($itemsOfType.length) {
}
});
Some really easy approach would be to add a status variable for each possible content type and check it:
$( document ).ready(function() {
var _image = true,
_video = true;
$( ".item" ).each(function() {
if ($(this).hasClass( "image" ) && _image) {
_image = false;
alert('image found');
};
if ($(this).hasClass( "video" ) && _video) {
_video = false;
alert('video found');
};
});
});
You can do this.
if($(".image").length>0)
{
alert("image Found")
//generate button
}
JSFIDDLE http://jsfiddle.net/rWrCA/
You can easily use an array for the types and another array for whether or not those exist. That looks something like:
$(document).ready(function () {
// a list of all types and a list of types that were found
var allTypes = ["image", "video", "gallery"];
var typesFound = [];
// loop over all items and add types to the list of found types
$(".item").each(function () {
for (var idx = 0; idx < allTypes.length; idx++) {
if ($(this).hasClass(allTypes[idx])) {
if (typesFound.indexOf(allTypes[idx]) < 0) {
typesFound.push(allTypes[idx]);
}
}
}
});
// as in the original code - prove this worked by displaying alerts!
for (var idx = 0; idx < typesFound.length; idx++) {
alert(typesFound[idx] + ' found');
}
});
I think that should do it!
Make an object in your jQuery ready function. When finished looping, write your buttons using the buttons object.
$(document).ready(function () {
var buttons = {};
$(".item").each(function () {
if ($(this).hasClass("image")) {
buttons.image = 1;
};
if ($(this).hasClass("video")) {
buttons.video = 1;
};
});
// write buttons
for (var type in buttons) {
$('<button/>', {
text: type,
id: 'btn_'+ type,
click: function () { alert('hi'); }
}).appendTo('body');
}
})
I'm building a site for someone and on the Admin side there is a "Manage Users" page to manage the website's users. Here is my two functions to suspend and unsuspend (and for the alert):
var admin = {
alert: (function(msg,dur) {
if(!dur || dur == null) {
dur = 1500;
}
$('#alert_box2').remove();
$('body').append('<div id="alert_box2" style="width: 100%; height: 9px; top: -17px; left: 0; position: absolute; text-align: center; z-index: 5;"><div id="alert_box_inner2"></div></div>');
$('#alert_box2').show(0, function() {
if(dur!=='none') {
$('#alert_box_inner2').html(msg).stop(true, true).fadeIn(800).delay(dur).fadeOut(800, function() {
$('#alert_box2').remove();
});
}
else {
$('#alert_box_inner').html(msg).show();
}
});
}),
suspendUser: (function(id) {
admin.alert('Please wait...',20000);
$.get('user_more_actions.php?action=suspend&nolightbox=1&id='+id, function(data,textStatus) {
setTimeout(function() {
if(textStatus=='success') {
if(data.indexOf('suspended') > -1) {
name = data.replace('suspended ','');
admin.alert(name+' is now suspended.',2500);
$('#status_'+id).html('<strong style="color: red;">Suspended</strong>');
$('#suspend_'+id).attr('id','unsuspend_'+id).text('Unsuspend').removeClass('suspend').addClass('unsuspend');
}
else {
admin.alert('Sorry, there was an error. <span class="s_link" onclick="$(\'#suspend_'+id+'\').click();">Try again</a>','none');
}
}
else {
admin.alert('Sorry, there was an error. <span class="s_link" onclick="$(\'#suspend_'+id+'\').click();">Try again</a>','none');
}
}, 500);
});
}),
unsuspendUser: (function(id) {
admin.alert('Please wait...',20000);
$.get('user_more_actions.php?action=unsuspend&nolightbox=1&id='+id, function(data,textStatus) {
setTimeout(function() {
if(textStatus=='success') {
if(data.indexOf('unsuspended') > -1) {
name = data.replace('unsuspended ','');
admin.alert(name+' is no longer suspended.',2500);
$('#status_'+id).html('<strong style="color: green;">Active</strong>');
$('#unsuspend_'+id).attr('id','suspend_'+id).text('Suspend').removeClass('unsuspend').addClass('suspend');
}
else {
admin.alert('Sorry, there was an error. <span class="s_link" onclick="$(\'#unsuspend_'+id+'\').click();">Try again</a>',20000);
}
}
else {
admin.alert('Sorry, there was an error. <span class="s_link" onclick="$(\'#unsuspend_'+id+'\').click();">Try again</a>',20000);
}
}, 500);
});
})
};
And the code that triggers the functions when a Suspend or Unsuspend link is clicked:
$('.suspend').each(function() {
$(this).live('click', function(e) {
e.preventDefault();
var id = $(this).attr('id').replace('suspend_', '');
admin.suspendUser(id);
});
});
$('.unsuspend').each(function() {
$(this).live('click', function(e) {
e.preventDefault();
var id = $(this).attr('id').replace('unsuspend_', '');
admin.unsuspendUser(id);
});
});
Everything is working ok, except when I click again it messes up. When a Suspend link is clicked, it changes to Unsuspend (and changes the ID). But then if I click Unsuspend it doesn't work, and it is calling the admin.suspend() function instead of admin.unsuspend() (and the ID isn't being passed so the name isn't displayed):
When the class and the ID is changed it should call either the admin.suspend(id_here) or admin.unsuspend(id_here); but it isn't.
Does anyone know why this is happening? Thanks in advance and I'm sorry that this post is long.
I've fiddled with it. Hope this helps:http://jsfiddle.net/wKGKu/
Update: After reading your concerns for .each, I've updated the code to demonstrate it isn't needed: http://jsfiddle.net/wKGKu/2/
I believe the way you wrote your live bindings is incorrect, they should have been bound like this:
$('.suspend').live('click', function(e) {
e.preventDefault();
var id = $(this).attr('id').replace('suspend_', '');
admin.suspendUser(id);
});
$('.unsuspend').live('click', function(e) {
e.preventDefault();
var id = $(this).attr('id').replace('unsuspend_', '');
admin.unsuspendUser(id);
});
I simplified fiddle showing the working code at: jsFiddle
You are attaching events to suspend/unsuspend classes, but your AJAX callback is modifying id attribute. Also you are horribly misusing live(). In the end your handler is already attached to the link and doesn't change after your AJAX calls.
Solution is to
1) leave ID's alone - you are only confusing yourself by modifying them
2) rewrite event handler to either not do each() or not use live - put together completely defeats purpose behind live()
$('.suspend').live('click', function(){
var id = $(this).attr('id').replace('suspend_', '');
admin.suspendUser(id);
return false;
});
$('.unsuspend').live('click', function(e){
var id = $(this).attr('id').replace('suspend_', '');
admin.unsuspendUser(id);
return false;
});
I'm trying to use jquery to switch between three images once you click on the image. Upon clicking on the third image, it switches it back to the first picture.
Is there a way to adapt the following to switch between more than two pictures, and for it to allow more than it switching once?
jQuery
$(document).ready(function() {
$("#clickMe").click(function() {
$("#myimage").attr({src : "picture2.png"});
});
});
HTML
<div id="clickMe"><img id="myimage" src="picture1.png" /></div>
Thanks.
This should do that:
$(document).ready(function() {
$("#clickMe").click(function() {
var src = $('#myimage').attr('src');
//if the current image is picture1.png, change it to picture2.png
if(src == 'picture1.png') {
$("#myimage").attr("src","picture2.png");
//if the current image is picture2.png, change it to picture3.png
} else if(src == "picture2.png") {
$("#myimage").attr("src","picture2.png");
//if the current image is anything else, change it back to picture1.png
} else {
$("#myimage").attr("src","picture2.png");
}
});
});
This works:
$(document).ready(function () {
var i = 1; // Used to keep track of which image we're looking at
$("#clickMe").click(function () {
i = i < 3 ? i + 1 : 1;
$("#myimage").html("picture#.png".replace("#", i));
});
});
Online demo: http://jsbin.com/afito
This is link might be helpful
http://www.sohtanaka.com/web-design/fancy-thumbnail-hover-effect-w-jquery/