I finally found a select menu plugin smart enough to work in IE comparability mode and also allows me to fire an event before the menu options are displaced. This awesome plugin is called jQuery Selectric.
I need to fire an event before the options are displayed and I want to make an ajax request which tells me which option I should enable/disable.
I was able to make the ajax request before the menu opens. but I am having hard time trying to disable the options. The options are always enabled even after I disable them.
I tried to use the $('select').selectric('refresh'); on ajax sucess but his cause a problem that the menu will never get a chance to open because just before it open the ajax request will close it again.
How can I disable options on the fly?
Here is what I have done
$('#MasterWrapUps').selectric();
$('#MasterWrapUps').on('selectric-before-open', function (e) {
var status = "noattempt";
$.ajax({
type: "GET",
url: "/getStatus",
dataType: "json",
cache: false,
success: function (data) {
if ( data && ! $.isEmptyObject(data) ) {
status = data.status;
}
attrWrapUpMenu(status);
}
});
});
function attrWrapUpMenu(status)
{
$('.dispositionMenuOption').each(function (index, element) {
var option = $(element);
if ( customIsAllowed(status, option) ) {
option.attr("disabled", false);
} else {
if( option.attr('selected') ) {
//At this point we know the option that is select can't be used, select the default value
$('#MasterWrapUps').val('0')
}
option.attr("disabled", true);
}
});
}
Since you cannot know how long the ajax is going to take to complete, I can think of two possible solutions:
The first is to display a hidden <div> over the input and, on click, show a loading image inside it. Then wait until the ajax is over to finally hide the div and trigger a click event on the updated <input>, or ....
You can launch the update function as soon as the page loads and keep the <select> disable until the ajax is complete.
I recommend the second one, but there might be other solutions. If you need any help implementing any of these you should open a new question.
Update
Since you want to do it after the click, the best I could come up with was this solution. It has the drawback that if the ajax takes too long and the user leaves the select input, it will reopen anyway. But I think this could be fixed with some focus/class verification. I'll leave that in your hands.
I must remind you that this kind of UI might leave some users (the one with bad connection) confused about what is available or not.
$('#MasterWrapUps').selectric().on('selectric-init', function () {
$('#MasterWrapUps').selectric('open');
});
$('.selectric').on('click', function () {
setTimeout(function () { /* fake ajax */
$('option:first-child').attr('disabled', true);
$('#MasterWrapUps').selectric('refresh');
}, 600 );
});
<!DOCTYPE html>
<html>
<head>
<script src="https://code.jquery.com/jquery-1.11.3.js"></script>
<script src="http://lcdsantos.github.io/jQuery-Selectric/jquery.selectric.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<link rel="stylesheet" href="http://lcdsantos.github.io/jQuery-Selectric/selectric.css">
<style>.selectric-above .selectric-items {top: 100%; bottom: auto}</style>
</head>
<body>
<select id="MasterWrapUps">
<option value="ant">Ant</option>
<option value="bird">Bird</option>
<option value="cat">Cat</option>
</select>
</body>
</html>
I reported this as a bug in github https://github.com/lcdsantos/jQuery-Selectric/issues/109. The author of the plugin Leonardo Santos at came up with a smart hacky way to solve the problem.
He gave me this code
$('#MasterWrapUps').selectric();
var isOpen = false;
var selectricData = $('#MasterWrapUps').data('selectric');
$('#MasterWrapUps').on('selectric-open', function (e) {
var status = "noattempt";
if (!isOpen) {
selectricData.close();
$.ajax({
type: "POST",
url: "/echo/json/",
dataType: "json",
cache: false,
data: {
// emulate ajax delay on jsfiddle
delay: 1
},
success: function (data) {
if ( data && ! $.isEmptyObject(data) ) {
status = data.status;
}
attrWrapUpMenu(status);
}
});
}
});
function customIsAllowed() {
return !!(Math.random()+.5|0);
}
function attrWrapUpMenu(status) {
$('.dispositionMenuOption').each(function (index, element) {
var option = $(element);
if ( customIsAllowed(status, option) ) {
option.prop('disabled', false);
} else {
if( option.prop('selected') ) {
$('#MasterWrapUps').val('0');
}
option.prop('disabled', true);
}
});
isOpen = true;
selectricData.refresh();
selectricData.open();
isOpen = false;
}
The code can also be found in this fiddle https://jsfiddle.net/lcdsantos/rgaeqbp6/
I hope this code help someone :)
Related
I have a JavaScript function which makes an AJAX call to return some data, the call is triggered on a select list change event.
I have tried many ways to display my loader whilst it's waiting, as it currently pauses the select list and doesn't look very good from a customer point of view.
The problem is, no matter what way I've tried to get this to show, the AJAX call completes before the loader shows.
Current Code:
<select name="addresslist" class="form-select" id="edit-addresslist" onchange="selectAddress(this)">
<option value="none">-- Please select an address from the list below --</option>
//there are more options but this isn't important here
</select>
JS File
function selectAddress(data) {
var loader = document.getElementbyId('overlay-loader');
var selectedAddress = data.value;
var uprn = selectedAddress.split(',')[1];
loader.style.display = "block";
$.ajax({
url: '~/collectiondates',
async: false,
data: {
uprn: uprn
},
success: function (data) {
result = data;
}
This is one of the ways I have tried, I have also tried calling a separate function on the "onchange" event, a separate function within selectAddress and another function which shows the loader then calls "selectAddress" but nothing works, the AJAX function is always completed first and then shows the loader (when it's no longer needed).
The rest of the code does exactly as expected.
Thanks.
Hi you should use deferred promises including done,fail and always with your ajax call and even a settimeout if the request is that fast check the example I made in this jsfiddle, also here is a syntax example:
$( function() {
function AjaxCall(rID,rStatus,rComment){
return $.ajax({
url: 'request.php',
data: {
id: rID,
requisitionStatus: rStatus,
comment: rComment
},
type: "POST",
cache: false,
beforeSend: function() {
$("#requisitionStatusDialog").dialog('close');
$('#ajax_loader_my').show();
}
})
}
$( "#requisitionStatusDialog" ).dialog();
$("#yourbuttonInputId").on('click',function(event) {
AjaxCall().done(function(data,response){
var obj = JSON.parse(data);
if (obj.status == "success") {
alert('whe are on done!');
}
}).fail(function(data,response){
$("#updateDialog").dialog(' close');
}).always(function(data){
if(confirm('You have finished the request. Click OK if you wish to continue ,click Cancel to reload the page.'))
{
$('#ajax_loader_my').hide();
$("#requisitionStatusDialog").dialog('open');
}else{
location.reload();
}
});
});
} );
Hope it helps =)
You could try placing the AJAX code inside a setTimeout function, in case the backend is working too fast.
setTimeout(function(){
// put here your AJAX
}, 2000); // 2 seconds delay
It is probably showing later because you are not setting the css to hide after success/error is returned. If you are worried about the loader not showing before the ajax finishes, you can set a delay in the AJAX call using setTimeout function
Here is what I do.
HTML -
<select name="addresslist" class="form-select" id="edit-addresslist" onchange="selectAddress(this)">
<option value="none">-- Please select an address from the list below --</option>
//there are more options but this isn't important here
</select>
JS/JQ -
function selectAddress(data) {
openloader();
var selectedAddress = data.value;
var uprn = selectedAddress.split(',')[1];
$.ajax({
url: '~/collectiondates',
async: false,
data: {
uprn: uprn
},
success: function (data) {
closeloader();
result = data;
}
}
}
function openloader(){
$("#overlay-loader").show();
}
function closeloader(){
$("#overlay-loader").hide();
}
CODE:
<span class="clickable" id="span_resend">Resend</span>
<script>
$('#span_resend').click(function (e) {
e.preventDefault();
var save_this = $(this);
var middle_this = $('<span class="loader">now_loading</span>');
$(this).replaceWith(middle_this)
$.ajax({
url:'/ajax/',
type:'post',
dataType:'json',
cache:false,
data:{
com: 'some',
},
success:function (data) {
console.log(data)
if (data.res === 'success'){
middle_this.replaceWith(save_this)
}
}
});
})
</script>
It works well when I click resend first.
However cause of script tag, there will be term of now_loading and after loaded, then clicking #span_resend does not works well.
I think it's from that I did not bind click function well on #span_resend.
But I don't know how to do it.
How can I do this?
More explanation: This code is to get ajax response from server, and that ajax response takes some time, maybe 10~15 seconds. So I want to change my resend button to show that ajax is being called, at the same time user cannot click during the waiting of ajax response from server.
The Problem:
Here's what's happening in your code that isn't obvious right away. On first click, you create a jQuery object containing the clicked span, you save this to a variable and after your post completes, you then replace the temporary span with the value of the variable.
Seems like everything should be just fine, but what you've actually done is dynamically added a control to your HTML and while the html of the control is identical to the original span, it is not the same control.
Why does this matter?
Events. It's all about events. When you copy a control, you aren't copying those event listeners associated with it too. So when that event fires again, it looks for the original control and doesn't find it.
You can read in depth about events and event listeners here.
So great, what do you do about all this?
The Solution:
The answer here is to bind those events to a control that is higher than the one you're replacing and won't be replaced itself. So maybe your body tag, or even the document tag. Here's what that would look like in your code:
// Instead of this:
$('#span_resend').click(function (e) {
// Some code.
});
// Do this:
$(document).on('click', '#span_resend', function (e) {
// Some code.
});
This ensures that those event listeners aren't removed when you replace the control.
Here's a mock up of your code using this method:
$(document).on('click', '#span_resend', function (e) {
e.preventDefault();
var save_this = $(this);
var middle_this = $('<span class="loader">now_loading</span>');
$(this).replaceWith(middle_this)
$.ajax({
url:'https://reqres.in/api/users?delay=3',
type:'post',
dataType:'json',
cache:false,
data:{
com: 'some',
res: 'success'
},
success:function (data) {
if (data.res === 'success'){
middle_this.replaceWith(save_this);
}
}
});
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class="clickable" id="span_resend">Resend</span>
Hope that helps!
I recommend not replacing the button with a now loading but to hide it and show a separate loading indicator, then revert back once it's done
$(document).ready(function() {
$("#saveBtn").click(saveData);
});
function saveData() {
$('#saveBtn').hide();
$('#nowLoadingInd').show();
//AJAX here instead of timeout (just for demo purpose)
window.setTimeout(function() {
$('#saveBtn').show();
$('#nowLoadingInd').hide();
}, 10000);
}
#saveBtn {
display:inline-block;
background:green;
color:white;
border-radius:10px;
cursor:pointer;
padding:3px 5px
}
#nowLoadingInd {
color:gray
}
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body>
<div id="saveBtn">Save!</div>
<div id="nowLoadingInd" style="display:none">Now Loading...</div>
</body>
</html>
Alternatively, you can pass an element to your ajax options and reference it in the then callback with the this object:
$.ajax({
url:"yourUrl",
data:{your:"data"},
extraProperty:$('#yourElem')
}).then(function() {
this.extraProperty.show()
});
I have a project which involves live notification. So I stumbled upon using socket io but I didn't have enough time to learn it yet. So I tried doing it with AJAX and jQuery. Below is my code structure and I was wondering if this is gonna work with no drawbacks?
setInterval(function(){
if( !element.hasClass('processing') ){
element.addClass('processing');
$.ajax({
type: 'post',
dataType: 'json',
url: ajaxurl,
data: {},
success: function( response ){
/* Success! */
element.removeClass('processing');
}
});
}
}, 2500);
Some Extra Info
The way you described will work. From Experience I would just like to point out some things.
I usually do a recursive function, allows you to wait your interval between ajax calls and not a fixed rate. //OPTIONAL BUT DOES GIVE THE SERVER SOME BREATHING ROOM.
Use window.setTimeout() with an isActive flag. //ALLOWS YOU TO STOP POLLING FOR WHATEVER REASON, AND BECAUSE FUNCTION IS RECURSIVE START UP AGAIN IF NEED BE
For Sake of being thorough, I found it is always a good idea to handle the error case of the $.ajax() post. You could perhaps display some message telling the user he is no longer connected to the internet etc.
Some Sample Code:
var isActive = true;
$().ready(function () {
//EITHER USE A GLOBAL VAR OR PLACE VAR IN HIDDEN FIELD
//IF FOR WHATEVER REASON YOU WANT TO STOP POLLING
pollServer();
});
function pollServer()
{
if (isActive)
{
window.setTimeout(function () {
$.ajax({
url: "...",
type: "POST",
success: function (result) {
//SUCCESS LOGIC
pollServer();
},
error: function () {
//ERROR HANDLING
pollServer();
}});
}, 2500);
}
}
NOTE
This is just some things I picked up using the exact method you are using, It seems that Web Sockets could be the better option and I will be diving into that in the near future.
Please refer :
Jquery : Ajax : How can I show loading dialog before start and close after close?
I hope this could help you
$("div.add_post a").click(function(){
var dlg = loadingDialog({modal : true, minHeight : 80, show : true});
dlg.dialog("show");
$.ajax({
url : "/add.php",
complete : function (){
dlg.dialog("hide");
}
});
return false;
});
//--Loading dialog
function loadingDialog(dOpts, text = "пожалуйста подождите, идет загрузка...")
{
var dlg = $("<div><img src='/theme/style/imgs/busy.gif' alt='загрузка'/> "+text+"<div>").dialog(dOpts);
$(".ui-dialog-titlebar").hide();
return dialog;
}
I am working on a project that I wanted to use jquery for, however it's come to light that it is mandatory to write in dojo, I've never worked with it before and am having some trouble reading up online as to how to use it.
Below is the code I was using:
<script>
window.onload = function() {
getManager($("#team_name").val());
}
function getManager(team) {
$.ajax({
type: "POST",
url: "getManager.php",
data: {team:team}
}).done(function( manager ) {
$("#manager_name").val(manager);
});
}
</script>
Essentially, there is a drop down <selection> field, when selected (and upon window loading) it should prefill the read-only box below it based upon which team is listed. See the below screenshot for a better idea:
I the above code works fine with jquery, I can't seem to find the equivalent for dojo.
This is the html for the selection field:
<select name="team" id="team_name" onchange="getManager(this.value)" type="text" readonly>
and this is the html code for text input field:
<input name="manager_name" id="manager_name" type="text" readonly/>
The code must be written in dojo as the rest of the page does, unless there is a way to override the dojo with jquery?
window.onload = function() {
getManager(document.getElementById('team_name').value);
}
function getManager(team) {
require(["dojo/_base/xhr"], function(xhr){
xhr.post({
url:"getManager.php",
timeout: 4000,
content: { team:team },
load: function( manager ){
document.getElementById("#manager_name").value = manager;
}
});
});
}
If you are using dojo 1.7 and above no need to use window.onload() or even dojo.addOnLoad() - I think this is what you are looking for
require(['dojo/dom', 'dojo/domReady!', function(dom) {
getManager(dom.byId('team_name').value);
});
function getManager(team) {
require(['dojo/request/xhr', 'dojo/dom'], function(xhr, dom) {
xhr.post('getManager.php', {
method: 'POST',
data: {team: team}
}).then(function(manager) {
dom.byId('manager_name').value = manager;
}, function(error) {
console.error('couldn\'t fetch manger!');
});
});
}
domReady! - is an AMD loaded plugin that will wait until the DOM has finished loading before returning
I'm using bsmSelect jQuery plugin. Basically, what it does is changing the way a select-multiple is rendered to make easier to pick up the options. It hides the select element and shows a list instead.
So, first of all I'm applying the plugin function to my select-multiple element:
$(document).ready(function() {
...
$('#my_select_multiple').bsmSelect({
plugins: [$.bsmSelect.plugins.sortable()],
title: 'Add',
removeLabel: 'Remove'
});
...
});
On the other way, I have another select element (this one is simple) which has an ajax request bind to its change event. This ajax request get new #my_select_multiple options depending on the select simple value. Ajax response is the new HTML for #my_select_multiple options. So I have:
function getNewOptions(val) {
var r = $.ajax({
type: 'GET',
url: /*My URL*/
}).responseText;
return r;
}
...
$(document).ready(function() {
...
$('#my_select_simple').change() {
$('#my_select_multiple').html(getNewOptions($(this).val()));
}
...
});
AJAX is working as expected. New options are got correctly and they are inserted into #my_select_multiple (which is hidden by bsmSelect plugin, but I can check it with Firebug). But bsmSelect didn't realize new changes and doesn't get updated.
So, I think what I want is to reapply $('#my_select_multiple').bsmSelect(); with its new options.
I've been looking around a little bit and here is what I have tried.
1. I've tried to call again the funcion with the success and complete (one at time) of the AJAX request. Didn't work:
function getNewOptions(val) {
var r = $.ajax({
type: 'GET',
url: /*My URL*/,
success: function() { $('#my_select_multiple').bsmSelect(); }
}).responseText;
return r;
}
2. I've tried to bind the function with the on jQuery function. Didn't work:
$('#my_select_simple').on('change', function() {
$('#my_select_multiple').bsmSelect();
});
3. I've tried 1 and 2 removing previosly the HTML generated by bsmSelect. Didn't work.
Thank you very much.
UPDATE: The exact code
First I have a global.js file which apply bsmSelect plugin to some select multiples (.quizzes):
$('.quizzes').bsmSelect({
plugins: [$.bsmSelect.plugins.sortable()],
title: 'Add',
removeLabel: 'Remove'
});
And then, in the php file I define the updateQuizzes function and bind it to the select simple (project_id) change event:
<script type="text/javascript">
function updateQuizzes(project_id) {
var r = $.ajax({
type: 'GET',
url: '<?php echo url_for('event/updateQuizzes')?>'+'<?php echo ($form->getObject()->isNew()?'':'?id='.$form->getObject()->getId()).($form->getObject()->isNew()?'?project_id=':'&project_id=')?>'+project_id,
success: function() { $('.quizzes').bsmSelect({
plugins: [$.bsmSelect.plugins.sortable()],
title: 'Add',
removeLabel: 'Remove'
}); }
}).responseText;
return r;
}
$('#project_id').change(function(){
$('.quizzes').html(updateQuizzes($(this).val()));
});
</script>
As I told, the AJAX request works without problems, but not the calling bsmSelect the second time...
Not sure if this is what the problem is, but you could try
$('#my_select_simple').change() {
$('#my_select_multiple').html(getNewOptions($(this).val())).trigger('change');
}
This triggers a change event on select_multiple, and might fire bsmSelect. I'm not sure what the problem here is exactly, but that's the best I can come up with.
I think you want to set your HTML in the success of the Ajax call, something like:
function loadNewOptions(val) {
$.ajax({
type: 'GET',
url: /*My URL*/,
success: function(data) {
$('#my_select_multiple').html(data).bsmSelect();
}
});
}
And then calling like:
$('#my_select_simple').change() {
loadNewOptions($(this).val());
}
$(document).ready(function() {
$('#my_select_simple').change() {
$('#my_select_multiple').load("your Url", function(){
$('#my_select_multiple').bsmSelect();
});
}
});
something like this should work.
.load will put whatever your url returns into #my_select_multiple
the first parameter is the url to load, and the 2nd is a function to call when it is done. which is where you need to set up your fancy selector.
Ok, I opened a ticket and bsmSelect developer has answered me in minutes. Great!
To let bsmSelect know about its select changes, you have to trigger a change event on the select. There is no need to call bsmSelect again.
So it can be that way:
function loadNewOptions(val) {
var r = $.ajax({
type: 'GET',
url: /*My URL*/,
success: function(data) {
$('#my_select_multiple').html(data).trigger('change');
}
}).responseText;
return r;
}
$('#my_select_simple').change(function() {
loadNewOptions($(this).val());
});