I added some hide and slide functions to a website so that as each product attribute was selected the next one would slide out. This worked fine until the customer added additional attributes to SOME products. The additional attribute is causing me problems because i can't add a second slide function trigger without making it trigger two functions on these products.
The original code i used is
$('.wrapperAttribsOptions11').hide();
$('.sizeRadio').click(function () {
$('.wrapperAttribsOptions11').slideDown(800);
});
The client then added an attribute id4 so i added
$('.wrapperAttribsOptions4').change(function () {
$('.wrapperAttribsOptions11').slideDown(800);
});
But this means that on pages where BOTH attributes are in use option11 is sliding down when .sizeRadio is clicked and not when option4 is changed.
In short, is it possible to make it function so that if .wrapperAttribsOptions4 is present then
$('.sizeRadio').click(function () {
$('.wrapperAttribsOptions11').slideDown(800);
});
is ignored.
I hope that's clear enough.
I resolved this by using
if ($('.wrapperAttribsOptions4').length != 0) {
so the whole code segment becomes
$('.wrapperAttribsOptions11').hide();
if ($('.wrapperAttribsOptions4').length != 0) {
$('.wrapperAttribsOptions4').change(function () {
$('.wrapperAttribsOptions11').slideDown(800);
});
}else
$('.sizeRadio').click(function () {
$('.wrapperAttribsOptions11').slideDown(800);
});
Related
I am integrating a front end html theme with a Laravel app and I am running into an issue with turbolinks not allowing Javascript to append div classes. This is causing the background images to only be displayed on refresh.
<div class="intro-banner" data-background-image="/storage/images/hero.jpg">
<div class="container">
custom.js
/*----------------------------------------------------*/
/* Inline CSS replacement for backgrounds
/*----------------------------------------------------*/
function inlineBG() {
// Common Inline CSS
$(".single-page-header, .intro-banner").each(function() {
var attrImageBG = $(this).attr('data-background-image');
if(attrImageBG !== undefined) {
$(this).append('<div class="background-image-container"></div>');
$('.background-image-container').css('background-image', 'url('+attrImageBG+')');
}
});
} inlineBG();
// Fix for intro banner with label
$(".intro-search-field").each(function() {
var bannerLabel = $(this).children("label").length;
if ( bannerLabel > 0 ){
$(this).addClass("with-label");
}
});
// Photo Boxes
$(".photo-box, .photo-section, .video-container").each(function() {
var photoBox = $(this);
var photoBoxBG = $(this).attr('data-background-image');
if(photoBox !== undefined) {
$(this).css('background-image', 'url('+photoBoxBG+')');
}
});
It looks like this code is only run once: on the initial page load. To get it working for every page load, you will need to run it on turbolinks:load. As the script also appends elements to the page, you need to be careful that you don't end up with unnecessary duplicate elements. Turbolinks stores a copy of the page in its final state before a visitor navigates away. This cached copy will include any appended HTML. So be sure your code checks for the presence of the appended elements before appending, or remove the elements before they are cached.
The following takes the latter approach, by removing elements on turbolinks:before-cache:
/*----------------------------------------------------*/
/* Inline CSS replacement for backgrounds
/*----------------------------------------------------*/
$(document).on('turbolinks:load', function () {
$(".single-page-header, .intro-banner").each(function() {
var attrImageBG = $(this).attr('data-background-image');
if(attrImageBG !== undefined) {
$(this).append('<div class="background-image-container"></div>');
$('.background-image-container').css('background-image', 'url('+attrImageBG+')');
}
});
// Fix for intro banner with label
$(".intro-search-field").addClass(function () {
if ($(this).children("label").length) return "with-label";
});
// Photo Boxes
$(".photo-box, .photo-section, .video-container").css('background-image', function () {
return 'url('+$(this).attr('data-background-image')+')'
})
});
$(document).on('turbolinks:before-cache', function () {
$(".single-page-header, .intro-banner").each(function() {
$(this).children(".background-image-container").remove();
});
});
I have also tidied up some of the jQuery code. Many jQuery functions accept functions as arguments, which simplifies things somewhat, and removes the need to iterate over a jquery selection with each.
Finally, wrapping lots of snippets in $(document).on('turbolinks:load', function () {…} is not great practice as creates a dependency on Turbolinks, and if you ever decided to move to something else, you have to update every place where this is called. If you're feeling adventurous, you may want to create a mini-framework like the one I create here: https://stackoverflow.com/a/44057187/783009
My goal:
To enable a user to load a template (which contains preset jquery, html and css) to allow them to click one of five dots to trigger an animation on an image.
My issue:
When I load more than one of these templates to my page, my animation value (margin-left in this case) applies double the number of times that there is an instance of this template on the page. If my template is loaded twice, the margin-left sets to a value, jumps to the correct value, then back before finally setting on the correct value. This means that if I was to add 10 instances to the page, it would take 20 times as long to get that last value.
Before testing I thought that my code would be ok, as due to the context and .once()function, I believed it would only fire once.
All html and CSS are functioning as expected, it's just the jQuery is an issue.
My code:
(function ($) {
Drupal.behaviors.click_the_dots = {
attach: function (context, settings) {
$('.wrapper_class', context).once('click_the_dots', function () {
// Prevent other buttons from being clickable until the
// previous animation is complete.
var animationDone = false;
function clickDots(dotNum) {
$('.dot_class_num_' + dotNum).click(function () {
// Setup context, to keep animations to the container in which the dots exist.
var findElem = $(this).parent().parent().parent().find('.inner_wrapper');
// Prevent other buttons from being clickable until the
// previous animation is complete.
if (animationDone === false) {
animationDone = true;
// Find the visible image.
var animatingImage = findElem.find('.dot_class_num_active');
// Find the image that will be animating in.
var thisImageAnim = findElem.find('.dot_num_img_src_' + dotNum);
if (animatingImage.is(thisImageAnim)) {
// Can't click on the same dot again, until another dot is clicked.
animationDone = false;
return;
} else {
// Animate out the already visible image.
// Remove the visible image class as it's going to be hidden.
findElem.find('.dot_class_num_active').removeClass('dot_class_num_active');
// Animate it to hide to the left.
animatingImage.animate({
marginLeft: '-10%',
opacity: 0
}, 280, 'easeInOutQuad');
// Animate in the image associated with the dot click
// Set the image css to be further right in order to animate to left at 0.
thisImageAnim.css('margin-left', '10%').delay(200).animate({
marginLeft: '0',
opacity: 1
}, 300, 'easeInOutQuad', function () {
// Set the now visible image to the visible image.
thisImageAnim.addClass('dot_class_num_active');
}).promise().done(function () {
// Now allow the other dots to be clicked.
animationDone = false;
});
}
}
});
}
// For each of the five dots.
for (i = 1; i <= 5; i++) {
clickDots(i);
}
});}};})(jQuery);
I would like to add as many instances of this jQuery as required, but only have the function be looped through once. I'm not sure how to check if this has already been done, or how to ensure that once it has been done at least once, it shouldn't happen again.
:)
I figured out what my issue was - I was attaching the behaviour to the wrapper class of my content, i.e. $('.wrapper_class', context).once... - this meant that it attached the behaviour to each instance of this class, of which there could be many.
What I did was attach the behaviour to a higher parent element, of which I knew there would be only one instance. It attaches just once and the code works perfectly.
Thanks to the commenters above who helped me realise my issue!
I have added a Select all / deselect all wrapper round a Select2 multi select control.
It works by looping through the options, pushing the values into an array then passing the array to the selct2 val as follows:
mySelect2.select2("val", mySelectedValuesArray);
This works fine in Chrome and in cases where there are not so many options to be selected. But in IE8 where they might be 100+ options the browser freezes as it attempts to render the selected values and I get multiple Stop Running this Script? alerts. I have had similar problems with IE8 when using expandable text boxes where the browser freezes whenever it has to increase the height of the textbox and assume its a quirk of the IE rendering engine. Anyway, in this case it renders the page unusable whenever you select all with anything more than 30 or 40 options.
I have tried creating the markup for the selected options container manually so as to just add it in one go, but, aside form then having to manually wire up the click events on each one to be able top remove them, Im finding when the selects change event fires it, select2 ends up removing the options anyway and i cant find a way round this.
Any ideas?
As an update here is my code
$(".filterIconContainer .filtericon").on("click",function () {
var $this = $(this);
var $associatedSelect = $("#" + $this.attr("data-associated-select"));
if ($associatedSelect.length == 0) {
$associatedSelect = $("#filterContainer div[data-tabid='" + $("#filterTabs li.active").attr("id") + "'] select");
}
if ($this.attr("data-action") == "select") {
var selected = [];
$associatedSelect.find("option").each(function (i, e) {
selected.push($(e).attr("value"));
});
setTimeout(function() {
$associatedSelect.select2("val", selected); // Browser throws stop running this script alert during select2 processing this line
$associatedSelect.change(); // call the change event to force any post change action
},5);
}
else {
$associatedSelect.select2('val', '');
$associatedSelect.change(); // call the change event to force any post change action
}
});
In the end I fixed this by making a change to the select2.js file
$(data).each(function () {
var i = this;
setTimeout(function () {
self.addSelectedChoice(i);
}, 0);
});
Wrapping the call to addSelectedChoice in a setTimeout allows IE to render the change without throwing a slow running script error.
I'm working with cookies to run or not run a jQuery animation someone else built:
$(function () {
$('div.transitional').click(function () {
$('div.intro').removeClass('hidden');
$('div.final').off('click');
});
ShowDiv($("div.transitional.hidden")[0]);
});
function ShowDiv(target) {
target = $(target);
target.removeClass('hidden');
target.delay(500).animate({
opacity: 1.0
}, 300, 'easeInExpo', function () {
ShowDiv($("div.transitional.hidden")[0]);
})
}
I have the cookie part working, but I'm confused about the anonymous function and the "ShowDiv" function.
What is each part doing?
Functionally, the animation makes visible a series of pictures, then the whole site. I want to skip the animation and just make the whole site visible (if cookies='visited'.) I'd like to do this without rewriting the animation script.
Here's a link: http://claytonsalem.com/bottlecap.
What happens now is if you have the cookie the animation doesn't run and everything is hidden.
That script only fades in elements, one after the other. If you want to skip that, use something like this in the anonymous function (which is also known as a DOM ready handler) :
$(function() {
$('div.transitional').click(function() {
$('div.intro').removeClass('hidden');
$('div.final').off('click');
});
if(cookies === "visited") //Assuming you already have the variable set.
ShowDiv($("div.transitional.hidden")[0]);
else
$("div.transitional.hidden").css('opacity', 1).removeClass('hidden')
});
I will focus on how it works:
$("div.transitional.hidden")
This would select ALL elements with div.transitional.hidden, placing them in a list.
By placing [0] in the selector, we are picking ONLY the first element in this list.
Then, when the script begins to run, this element is modified by target.removeClass('hidden'), which removes the hidden class.
When the scripts ends, it calls the $("div.transitional.hidden")[0] selector again, but this time it will not include the previously selected element (because it no longer has the hidden class).
That's why the script show images one after the other: it removes the hidden class and selects the next remaining element.
You might refer to Karl's answer on how to show your whole site.
I am trying to create a form that has various hide/reveals in it and one of the last parts I need to do to this form is SHOW the payment information fields when only Credit Card is selected.
I have a test page setup here: http://www.faa.net.au/test/femmes-member-form.html
Process so far is:
Enter your details
Select Event Date
Selecting Member + 1 or more Guests ask for payment details
At the moment, I have displayed the 3 DIVs that I want to appear depending on the radio selection made but when I hide these, the code I have in place at present doesn't work.
Can anyone help me here at all please?
If you need the code, please let me know, with a number of different elements involved I didnt want to paste the whole thing on here, hopefully you can see the Source Code?
Here is the Javascript I have at present but not sure if its this that is wrong or if its clashing with something else?
<script type="text/javascript">
$(function() {
$('.cat_dropdown').change(function() {
$('#payMethod').toggle($(this).val() >= 2);
});
});
</script>
<script type="text/javascript">
$(document).ready(function () {
$(".payOptions").click(function () {
$(".paymentinfo").hide();
switch ($(this).val()) {
case "Credit Card Authorisation":
$("#pay0").show("slow");
break;
case "Direct Deposit":
$("#pay1").show("slow");
break;
case "Cash Payment (FAA Office)":
$("#pay2").show("slow");
break;
}
});
});
</script>
As per viewing code from View Souce and guessing that you have not added correct class in event handler. thus click event for radio is not getting invoked.
Change
$(".payOptions").click(function () {
to
$(".paymentmethod").click(function () {
You have not posted any source, but if you are using jQuery, you can simply do:
$(".commonclass").hide();
Provided that all 3 divs have the "commonclass" class.
Process goes something like this:
Start clean: hide all payment methods
Your radio inputs have paymentmethod class, so attach a change event listener to those elements
When one of the radios is selected, hide all of the payment methods, determine the one you want to show using index, and show that div
$('#pay0, #pay1, #pay2').hide();
$('input.paymentmethod').on('change', function(){
$('#pay0, #pay1, #pay2').hide();
var selected = $('input.paymentmethod').index($('input.paymentmethod:checked'));
$('#pay'+selected).show();
});
Used to jquery as like this
Css
#pay0, #pay1, #pay2{display:none;}
Jquery
$(document).ready(function(){
$('#payment').change(function(){
if($('#CAT_Custom_255277_0').attr('checked')){
$('#pay0').show();
$('#pay1').hide();
$('#pay2').hide();
}
else if($('#CAT_Custom_255277_1').attr('checked')){
$('#pay1').show();
$('#pay0').hide();
$('#pay2').hide();
}
else if($('#CAT_Custom_255277_2').attr('checked')){
$('#pay2').show();
$('#pay0').hide();
$('#pay1').hide();
}
});
});
Demo
As per my understanding, you're trying like below,
select value from dropdown, if the value !== "1" then show payment radio buttons
Based on the radio button selection, you want to show the respective div
From viewing your source code, it seems you're using jQuery lib and there use this
$(document).ready(function() {
$('input[type=dropdown]').on('change', function(){
if($(this).val() !== 1)
{
$('input[type=radio]').show();
}
}
$('input[type=radio]').on('change', function(){
if($(this).val() === "Credit Card Authorisation") {
$('#pay1').hide();
$('#pay2').hide();
$('#pay0').show();
}
else if($(this).val() === "Direct Deposit"){
$('#pay0').hide();
$('#pay2').hide();
$('#pay1').show();
}
else if($(this).val() === "Cash Payment (FAA Office)"){
$('#pay0').hide();
$('#pay1').hide();
$('#pay2').show();
}
});
});
Hope you understand.