Firefox won't update value on second click - javascript

So I've taken over a project that is almost ready to launch, a site where you can lease cars. I've uploaded the main part of the service here: http://erikblomqvist.se/junk/car/
Everything works smoothly except for the color changing function (the colored boxes under the headline Färgalternativ). It's supposed to update the price on the colors from brown to light gray (#4 - #8) – those are a bit more expensive, since they are in metallic.
In Chrome, this works as planned, but in Firefox, if I first select a metallic color, then a non-metallic, and THEN a the same metallic again, the price won't change back. It changes correctly the first time, but not the second time I click that metallic color.
In Safari, the price doesn't change at all (I'm guessing that if the Firefox problem gets solved, Safari gets solved as well).
The function is based on a data-name on the color boxes, that gets checked with this function:
$( '#car-colors .color' ).each(function() {
$( this ).on( 'click', function() {
selected_color = undefined;
var color_name = $( this ).data('name');
$( '#car-colors .color' ).not( this ).removeClass('selected');
$( this ).addClass('selected');
$( 'option', color_select ).each( function() {
if( $( this ).val() == color_name ) {
color_select.find( 'option' ).removeAttr('selected');
$( this ).attr('selected', 'selected');
}
});
$( '.selected-color-name' ).fadeIn();
$( '.selected-color-name span' ).html( color_name );
var selected_color = color_select.children(':selected');
checkSelectedColor(selected_color);
});
});
The variable color_select is defined as $( '#order-color-select' ).
The function checkSelectedColor is defined here:
function checkSelectedColor(selected_color) {
if( selected_color.data('is-metallic') == 'yes' ) {
color_checkbox.prop('checked', true);
} else {
color_checkbox.prop('checked', false);
}
color_input.val( selected_color.val() );
calculatePrice();
}
I've added stuff like selected_color = undefined to make sure that the variable is reseted, but after a color that has an <option data-is-metallic="yes"> (inside of #order-color-select) is selected a second time, it handles the value as "no" instead of "yes".
I can't get my head around on why this is, especially only in Firefox/Safari.
I've included the beautified version of the car functions here: http://pastebin.com/i5bup5rx
Appreciate any kind of help that could lead me in the right direction of getting this solved!
Thanks

I think the error is in setting the selected option in the select element using .attr().
Instead you can just set the value of the select element like
$('#car-colors .color').on('click', function() {
var color_name = $(this).data('name');
$('#car-colors .color').not(this).removeClass('selected');
$(this).addClass('selected');
$('#order-color-select').val(color_name);
$('.selected-color-name').fadeIn();
$('.selected-color-name span').html(color_name);
var selected_color = color_select.children(':selected');
checkSelectedColor(selected_color);
});

Related

Bootstrap Select with callback function

I have 2 Bootstrap Select dropdowns, one is a list of countries, and another is a list of states. The country list is static and is populated on page load. The list of states is only populated via the Bootstrap Change event and loads states based upon the country.
I need to be able to populate both these values at the same time. So the problem is I'm trying to set the state value when its undefined ,or hasn't loaded its options yet. I've tried doing a callback function within the ajax call, but that doesn't appear to work. I think another problem is that there is the initial change binding that is goofing it up.
I've written a fiddle for my foundation on where I'm experiencing the problem.
https://jsfiddle.net/jeffbeagley/DTcHh/37843/
The user is presented with a blank form that allows them to select the values, but the user can also load a saved form from the database ( hence the function labled as "select_data" ).
$(document).ready(function() {
$( "#country" ).on( 'changed.bs.select', function(e) {
change_country($( this ).val());
});
$( "#load" ).click(function() {
select_data();
});
});
This function is called anytime the user selects a country, the actual ajax call goes out to the database and returns all the appropriate states. I tried passing the state into this function and selecting it that way, but setting the country first would still fire the country change event and override it.
function change_country(country_id) {
$.ajax({
type: 'GET',
cache: false,
url: '/echo/jsonp/',
success: function(response) {
$( "#state" ).empty();
if(country_id == 1) {
$( "#state" ).append( "<option value=1>Oklahoma</option" );
$( "#state" ).append( "<option value=2>Missouri</option" );
} else {
$( "#state" ).append( "<option value=1>Ontario</option" );
$( "#state" ).append( "<option value=2>Quebec</option" );
}
$( "#state" ).selectpicker( "refresh" );
}
});
}
This is the function that grabs the saved form from the database and populates the values. With the way that its set, the expected outcome would be to select the country Canada, and the state Quebec
function select_data() {
var country_id = 2
var state_id = 2
$( "#country" ).selectpicker('val', country_id);
$( "#state" ).selectpicker('val', state_id);
}
Thanks for any help!
Might've figured it out.. sometimes you just need to strip out your code and simplify it to find an answer..
I replaced the code to populate the states outside of the event being called from the country being changed. Then I declared a callback function within this new function on success. I then moved the logic to select the country into the populate_state function ( this may not be appropriate )
You can see the updated fiddle here,
https://jsfiddle.net/jeffbeagley/DTcHh/37849/
So now the function select_data is as follows
function select_data() {
var country_id = 2
var state_id = 2
populate_states(country_id, function() {
$( "#state" ).selectpicker('val', state_id);
});
}
Edit ----
So with this, I ran into a call stack issue as it was still applying the original change event. So when the function select_data() is called, I unbind any events attached to the country selector, and rebind within the callback.
function select_data() {
var country_id = 2
var state_id = 2
$( "#country" ).off( 'changed.bs.select');
$( "#country" ).selectpicker('val', country_id);
populate_states(country_id, function() {
$( "#state" ).selectpicker('val', state_id);
$( "#country" ).on( 'changed.bs.select', function(e) {
change_country(e,$( this ).val());
});
});
}

Passing value to anonymous function in jQuery

I just updated the code and below is the full version of the code
If data returns 0 I want to get the text value of the selected option and append it to a div. Now data returns 0 and the div with class iconSuccess shows up but the value of $a cannot be gotten. I try to alert($a) or even alert(1) in the if condition statement but nothing happens. I need to get the value of $a in the if condition statement. How can I pass the value to the function that return data?
below is the full version of the code
$('#categoryList').change(function () {
$a = $("#categoryList option:selected").text();
$( ".iconSuccess" ).hide();
$("#categoryList5,#categoryList4,#categoryList3,#categoryList2" ).hide();
$.post( "/trobay/categories/default/lists?parent_id="+$(this).val(), function(data) {
if(data !=='0'){
$( "select#categoryList1" ).show();
$( "select#categoryList1" ).html( data );
}else{
$("#categoryList5,#categoryList4,#categoryList3,#categoryList2,#categoryList1" ).hide();
$( ".iconSuccess" ).show();
$( ".mydiv" ).append($a);
alert(1);
alert($a);
}
//alert($a);
});
});
The below image is a screenshot of the message i got back from the server and the green icon only shows up when data return "0"
I hope this put more light on the question and any help will be apppreciated

How to add links to tabbing order when they're made visible with CSS?

Links that are subject to display: none aren't in the default tabbing order. However, when they're revealed - e.g. CSS for a drop-down menu reveals a sub-menu when a parent link gains focus - they still aren't in the tabbing order. Presumably JavaScript is required, but simply setting tabindex="0" does nothing.
The problem here is that as soon as you tab off the "Top level page with child" link, the CSS is updated and the parent of the anchor becomes display:none before the anchor can receive focus. You will need to use JavaScript to solve this problem and delay the change in the CSS using a timeout until you can determine whether the loss of focus has resulted in the focus shifting to the child element.
Setting tabindex="0" when the parent is display:none will not help, display:none means that the content might as well not be in the document.
I've come up with a solution that basically works: http://codepen.io/gyrus/pen/waKjKv/ There seem to be some issues in IE, I'm working on that. But the general idea is:
Add a small delay before hiding the drop-down
Set a data attribute to flag whether any links inside the drop-down have focus, and check this before hiding
This is just the jQuery, check the pen for more:
jQuery( document ).ready( function( $ ) {
$( '.nav' ).on( 'mouseenter focusin', '.menu-level-0.menu-item-has-children', function( e ) {
var el = $( this );
// Show sub-menu
el.attr( 'aria-expanded', 'true' )
.find( '.sub-menu-wrapper' ).show();
}).on( 'mouseleave focusout', '.menu-level-0.menu-item-has-children', function( e ) {
var el = $( this );
// Only hide sub-menu after a short delay, so links get a chance to catch focus from tabbing
setTimeout( function() {
var smw = el.find( '.sub-menu-wrapper' );
if ( smw.attr( 'data-has-focus' ) !== 'true' ) {
el.attr( 'aria-expanded', 'false' );
smw.hide();
}
}, 100 );
}).on( 'focusin', '.sub-menu-wrapper', function( e ) {
var el = $( this );
el.attr( 'data-has-focus', 'true' );
}).on( 'focusout', '.sub-menu-wrapper', function( e ) {
var el = $( this );
el.attr( 'data-has-focus', 'false' );
// Hide sub-menu on the way out
el.hide().parents( '.menu-level-0' ).attr( 'aria-expanded', 'false' );
});
});

jQuery mobile nested-list plugin bug going back

I'm using the 'nested-list' plugin for jQuery Mobile, this one:
The problem is that when you use more than one level the plugins fails going back. For example, in the fiddle I have created I can go to 'Test 1.2.1' without problem, If I going back 1 level it works fine and I go to 'Test 1.2', but then if I tried to go up one level more (it was 'Test1') it goes up 2 levels (to 'Test').
I have checked the plugin code but I can't find the problem and I have left a message in the Git forum with no answer. Maybe someone could help me here.
Thanks in advance!
Fiddle
Looking at the plugin code, it is only designed for one level deep nesting. This is because the developer chose to remove created subpages each time you click on a parent LI. So when you get to the second level of depth, its parent has been removed from the DOM and you have to click the back button twice to get to the original page.
I have made some changes to the plugin code that should solve this problem:
In _attachBindings, I have commented out the line that removes previously created subpages:
_attachBindings: function() {
this._on({
"click": "_handleSubpageClick"
});
this._on( "body", {
"pagechange": function(){
if ( this.opening === true ) {
this.open = true;
this.opening = false;
} else if ( this.open === true ) {
//Don't remove the old LI
//this.newPage.remove();
this.open = false;
}
}
});
},...
Then in _handleSubpageClick, I check if the subpage already exists in the DOM (via data attribute added when creating the page). If not, we go through the existing code that creates the subpage, and then in the end I store the created subpage id in a data attribute on the parent LI. If it does exist we just navigate to that page.
_handleSubpageClick: function( event ) {
if( $(event.target).closest( "li" ).children( "ul" ).length == 0 ) {
return;
}
this.opening = true;
//see if we already created the subpage
var $li = $(event.target).closest( "li" );
var pid = $li.data("nextpageid");
if (pid && pid.length > 0){
this.pageID = pid;
} else {
this.newPage = $( this.options.page ).uniqueId();
this.nestedList = $( event.target ).children( "ul" )
.clone().attr( "data-" + $.mobile.ns + "role", "listview" )
.css( "display", "block" );
this.pageName = (
$( event.target.childNodes[0] ).text().replace(/^\s+|\s+$/g, '').length > 0 )?
$( event.target.childNodes[0] ).text() : $( event.target.childNodes[1] ).text();
this.pageID = this.newPage.attr( "id" );
// Build new page
this.newPage.append(
$( this.options.header ).find( "h1" ).text( this.pageName ).end()
).append(
$( this.options.content )
).find( "div.ui-content" ).append( this.nestedList );
$( "body" ).append( this.newPage );
//save subpage id as data attribute of the LI
$li.data("nextpageid", this.pageID);
}
$( "body" ).pagecontainer( "change", "#" + this.pageID );
}...
Here is your updated FIDDLE
I removed the external link to the plugin and instead copied all the code into the javascript pane and made the edits. You should be able to copy that code directly and use as the updated plugin. (Of course I did this quickly and have not rigorously tested it, so make sure it works for you).

Jquery Mobile 1.4 swipe demo in Chrome with mobile device

My question concerns the swipe event on a mobile device (I'm using a Nexus 7) with Chrome. I am working off the Jquery Mobile 1.4.2 demo which can be found here:
http://demos.jquerymobile.com/1.4.2/swipe-page/
I'll ask my question and copy the sample javascript below. I can get everything to work, both on my laptop (using Chrome) and on my tablet (using Firefox), but the swipe works maybe one out of ten times in Chrome with my tablet. Any advice? Thanks!
// Pagecreate will fire for each of the pages in this demo
// but we only need to bind once so we use "one()"
$( document ).one( "pagecreate", ".demo-page", function() {
// Initialize the external persistent header and footer
$( "#header" ).toolbar({ theme: "b" });
$( "#footer" ).toolbar({ theme: "b" });
// Handler for navigating to the next page
function navnext( next ) {
$( ":mobile-pagecontainer" ).pagecontainer( "change", next + ".html", {
transition: "slide"
});
}
// Handler for navigating to the previous page
function navprev( prev ) {
$( ":mobile-pagecontainer" ).pagecontainer( "change", prev + ".html", {
transition: "slide",
reverse: true
});
}
// Navigate to the next page on swipeleft
$( document ).on( "swipeleft", ".ui-page", function( event ) {
// Get the filename of the next page. We stored that in the data-next
// attribute in the original markup.
var next = $( this ).jqmData( "next" );
// Check if there is a next page and
// swipes may also happen when the user highlights text, so ignore those.
// We're only interested in swipes on the page.
if ( next && ( event.target === $( this )[ 0 ] ) ) {
navnext( next );
}
});
// Navigate to the next page when the "next" button in the footer is clicked
$( document ).on( "click", ".next", function() {
var next = $( ".ui-page-active" ).jqmData( "next" );
// Check if there is a next page
if ( next ) {
navnext( next );
}
});
// The same for the navigating to the previous page
$( document ).on( "swiperight", ".ui-page", function( event ) {
var prev = $( this ).jqmData( "prev" );
if ( prev && ( event.target === $( this )[ 0 ] ) ) {
navprev( prev );
}
});
$( document ).on( "click", ".prev", function() {
var prev = $( ".ui-page-active" ).jqmData( "prev" );
if ( prev ) {
navprev( prev );
}
});
});
$( document ).on( "pageshow", ".demo-page", function() {
var thePage = $( this ),
title = thePage.jqmData( "title" ),
next = thePage.jqmData( "next" ),
prev = thePage.jqmData( "prev" );
// Point the "Trivia" button to the popup for the current page.
$( "#trivia-button" ).attr( "href", "#" + thePage.find( ".trivia" ).attr( "id" ) );
// We use the same header on each page
// so we have to update the title
$( "#header h1" ).text( title );
// Prefetch the next page
// We added data-dom-cache="true" to the page so it won't be deleted
// so there is no need to prefetch it
if ( next ) {
$( ":mobile-pagecontainer" ).pagecontainer( "load", next + ".html" );
}
// We disable the next or previous buttons in the footer
// if there is no next or previous page
// We use the same footer on each page
// so first we remove the disabled class if it is there
$( ".next.ui-state-disabled, .prev.ui-state-disabled" ).removeClass( "ui-state-disabled" );
if ( ! next ) {
$( ".next" ).addClass( "ui-state-disabled" );
}
if ( ! prev ) {
$( ".prev" ).addClass( "ui-state-disabled" );
}
});
I've done the same experiment and I've observed similar results with my tablet (Nexus 7 - Google Chrome).
You should not use heavy frameworks like jQueryMobile if you are going to create a web app or a mobile website because even if these tools make your life easier at the end the result, especially on Android devices, will be slow and sluggish.
In other words you should create your own .css and .js.
If you need to manipulate the DOM very often you should also look for alternatives to jQuery.
I suggest that you use Zepto.js.
In the end, I decided to use the jQuery touchSwipe plugin and write my own code, works fine in different browsers and across devices. Some of this may not make sense without the HTML, but essentially I determine the direction of the swipe based on the variable that is passed into the method. Then, by getting various attributes and class names, I am turning on and off the display of the various divs that have previously loaded the JSON into them from another method. The way I do that is through substrings, where the last digit of the id is a number. If anyone has any comments about how this code could be more efficient, I'd be happy to hear your thoughts. Cheers.
function swipeLiterary() {
$("#read").swipe({
swipe:function(event, direction, distance, duration, fingerCount) {
switch (direction) {
case 'left':
var thisPage = $('.display').attr('id');
var nextPageNum = parseInt(thisPage.substring(8)) + 1;
var nextPage = thisPage.substring(0,8) + nextPageNum;
if (nextPageNum > 9) {
break
}
$('#' + thisPage).removeClass('display').addClass('nodisplay');
$('#' + nextPage).removeClass('nodisplay').addClass('display');
console.log(nextPage);
break;
case 'right':
var thisPage = $('.display').attr('id');
var prevPageNum = parseInt(thisPage.substring(8)) - 1;
var prevPage = thisPage.substring(0,8) + prevPageNum;
if (prevPageNum < 0){
break;
}
$('#' + thisPage).removeClass('display').addClass('nodisplay');
$('#' + prevPage).removeClass('nodisplay').addClass('display');
console.log(prevPage);
break;
case 'up':
console.log('up');
break;
}
//$(this).text("You swiped " + direction );
//console.log(this);
}
});
}

Categories

Resources