To follow on from a previous question here is a function used to determine what elements selected by Radio Buttons and Checkboxes are loaded on the right hand sign and what elements are loaded out via 'blank2.html'. The problem is it is loading the wrong elements in at the wrong points in time. Here is the code.
function product_analysis_global() {
$(':checked').each(function() {
var alt = $(this).attr('alt');
var title = $(this).attr('title');
if ($('#product_quantity_PRI_' + alt).attr('title') != title) {
$('#product_' + alt).load(title);
$('#product_quantity_PRI_' + alt).attr('title', title);
$('#product_quantity_PRI_' + alt).val($(this).val());
} else if ($('#product_quantity_PRI_' + alt).attr('title') != 'http://www.divethegap.com/update/blank2.html') {
$('#product_' + alt).load('http://www.divethegap.com/update/blank2.html');
$('#product_quantity_PRI_' + alt).attr('title', 'http://www.divethegap.com/update/blank2.html');
} else return false ;
});
}
Take a look at its implementation here and you will see precisely what I mean.
http://www.divethegap.com/update/diving-trips/adventure-training and the click on BEGINNERS to load in the form.
I believe the problem lies in the $(this).attr('alt') and $(this).attr('title') collecting only one attribute for the box checked not all of them.
An additional feature that I would like this code to have is to treat a disabled checkbox or radio button at an unchecked box and load it out via 'blank2.html'
$.load fires off an asynchronous request, therefore subsequent statements will be evaluated before the request finishes (at least in the vast majority of cases).
You will need to do what you want within its success callback, to ensure that it is guaranteed to happen once $.load has completed:
// make sure you make the current context available within the callback
var that = this;
$('#product_' + alt).load(title, function() {
$('#product_quantity_PRI_' + alt).attr('title', title);
$('#product_quantity_PRI_' + alt).val($(that).val());
});
Related
Ok, here's my issue. The following code works as intended, but I have this nagging feeling that I should be able to solve the problem more succinctly. I'm writing a script that will allow a person to pan/tilt/zoom an IP camera using custom controls on an html page. I've layed out the direction icons in a numberpad-style arrangement representing up, down, left-up etc... like so:
1 2 3
4 _ 6
7 8 9
When the user holds the mousedown on an icon, the img is swapped out for an active version and the command to begin the action is sent to a php cURL script, along with the respective direction (icon id). When the mouse is released the image is again swapped for the inactive version and the command is sent to the cURL script to stop moving in that same direction.
This works as long as the mouse is kept hovering over the same icon that was initially selected. If the person let the mouse leave the icon and then releases it, the second function checks to see if any of the directions are currently activated, inactivating them and sending a respective stop command to the cURL script.
Is there a way to actually accomplish this using one function?
// PTZ MOVEMENT / IMAGE SWAP
$('.nav-control').on('mousedown mouseup', '.ptz-cmd', function(e){
var thisCmd = $(this).attr('id'); // 1 - 9, designating numberpad style of movement
var thisAction = $(this).attr('action') // pantilt or zoom
if (e.type == 'mousedown') {
$(this).attr('src','img/' + thisCmd + 'h.png'); // example: 1h.png = active icon, 1b.png = inactive icon
$('#ptz').load("ptz.php?action=" + thisAction + "&cmd=" + thisCmd); // movement is handled by php cURL script and 'loaded' into a hidden div
} else {
$(this).attr('src','img/' + thisCmd + 'b.png');
$('#ptz').load("ptz.php?action=" + thisAction + "&cmd=stop"); // stop the movement or zoom for this direction...
}
});
// CANCEL MOVEMENT AND REPLACE IMAGE IF MOUSE LEAVES ICON AND IS RELEASED
$('.nav-control').on('mouseleave', '.ptz-cmd', function(e){
$('#ptz').load("ptz.php?action=pantilt&cmd=stop");
$('.ptz-cmd:not([action=preset])').each(function(){
if($(this).attr('src').substring(5) == "h.png"){
var whichDirection = $(this).attr('src').substring(0,5);
$(this).attr('src',whichDirection + 'b.png')
}
});
});
Absolutely:
var $ptz = $('#ptz');
$('.nav-control').on({
mousedown:function(){
var self = this,
thisCmd = self.id,
thisAction = self.action;
self.src = 'img/' + thisCmd + 'h.png';
$ptz.load("ptz.php?action=" + thisAction + "&cmd=" + thisCmd);
},
mouseup:function(){
var self = this,
thisCmd = self.id,
thisAction = self.action;
self.src = 'img/' + thisCmd + 'b.png';
$ptz.load("ptz.php?action=" + thisAction + "&cmd=stop");
},
mouseleave:function(){
$ptz.load("ptz.php?action=pantilt&cmd=stop");
$('.ptz-cmd').filter(':not([action=preset])').each(function(){
var self = this,
src = self.src;
if(src.substring(5) === 'h.png'){
self.src = src.substring(0,5) + 'b.png';
}
});
}
},'.ptz-cmd');
Changes:
By using the object form of .on(), you can consolidate these three events into a single binding
By splitting mousedown and mouseup, there is less runtime parsing (no checking events, less code per run of each, etc)
Using vanilla JS vs jQuery is faster, used for items like src,id, and action.
Use of === instead of == is more strict, and therefore more standards-compliant
Caching of $('#ptz') at top will save extra DOM scrapes
This isn't tested, but the theory behind it is sound. Even if you only take away the first point, you will have consolidated all your event bindings into a single call, and delegated appropriately.
Alternative:
var $ptz = $('#ptz');
$('.nav-control').on({
'mousedown mouseup':function(e){
var self = this,
cmdImg = self.id,
thisAction = self.action,
img = 'h',
thisCmd = cmdImg;
if(e.type === 'mouseup'){
img = 'b';
thisCmd = 'stop';
}
self.src = 'img/' + cmdImg + img + '.png';
$ptz.load("ptz.php?action=" + thisAction + "&cmd=" + thisCmd);
},
mouseleave:function(){
$ptz.load("ptz.php?action=pantilt&cmd=stop");
$('.ptz-cmd').filter(':not([action=preset])').each(function(){
var self = this,
src = self.src;
if(src.substring(5) === 'h.png'){
self.src = src.substring(0,5) + 'b.png';
}
});
}
},'.ptz-cmd');
This just maintains the mouseup and mousedown combination, checking the event type to see which it is. Its every-so-slightly slower, but consolidates the codebase into a single function, making maintenance a bit easier. Notice in this option when you have two different events you need to make it a string, e.g. 'mouseup mousedown' vs mouseup mousedown ... you can only use the object names if there is a single object.
Either one of these options should put you on the right track.
See this form - http://schnell.dreamhosters.com/form.php
This form has a portion of it where you enter data and can choose to add more of the same data by clicking a button called 'Add A Site' and it will make another of that section to enter another site. This is the jQuery that performs the duplication...
$(function () {
var sites = 1;
var siteform = $("#site1").html();
$(".addsites").live("click", function(e) {
e.preventDefault();
sites++;
$("#events").append("<div id='site" + sites + "'>"
+ "<br /><hr><br />"
+ siteform
+ "<center><button class='removesites' title='site"
+ sites + "'>Remove This Site</button><br />"
+ "<button class='addsites'>Add Another Site</button>"
+ "</center></div>");
});
$(".removesites").live("click", function(e) {
e.preventDefault();
var id = $(this).attr("title");
$("#" + id).remove();
});
});
The duplication works perfectly, but one thing that's bugging me is that when I have to enter data for someone claiming a LOT of sites, it gets very annoying having to repeat same or similar parts of this section of the form (like every site is in the same city, on the same day, by the same person, etc.) So I had the idea that with each duplication, the values of the form elements would also carry over and I just edit what's not the same. The current implementation only duplicates the elements, not the data. I'm not sure how to easily copy the data into new sections, and I can't find any jQuery tools to do that.
PS - This part isn't as important, but I've also considered using this same form to load the data back in for viewing/editing, etc. The only problem with this is that the reprinting of the form means that there will be a form section with the id "Site7" or something, but jQuery starts its numbering at 1, always. I've thought about using selectors to find the highest number site and start off the variable 'sites' at that number, but I'm not sure how. Any advice how to do this, or a better system overall, would be much appreciated.
You want to itterate over the input fields in siteform and store them in an object using their name attribute as a key.
Then after the duplication of the object you made and look for the equivelant fields in the new duplicated form ans set their values.
Somthing like this (not tested, just the idea)
var obj = new Object();
$("#site1 input").each(function(){
obj[this.id] = this.value;
);
// Dupicate form
$.each(obj, function(key, value){
$('#newform input[name="'+key+'"]').value = value;
});
Mind you these two each() functions differ from each other.
http://api.jquery.com/jQuery.each/
http://api.jquery.com/each/
You could consider using cloneNode to truely clone the previous site-div and (by passing true to cloneNode) all of its descendants and their attributes. Just know that the clone will have the same id as the original, so you'll have to manually set its id afterwards
Try this in your click-function
var clone = $("#site" + sites).clone(true, true); // clone the last div
sites++; // increment the number of divs
clone.attr('id', "site" + sites); // give the clone a unique id
$("#events").append(clone); // append it to the container
As Scuzzy points out in a comment jQuery does have its own clone() method (I don't use jQuery much, so I didn't know, and I didn't bother to check before answering). Probably better to use jQuery's method than the built-in cloneNode DOM method, since you're already using jQuery for event listeners. I've updated the code
The query to transfer values is quite simple (please, check the selector for all the right types on the form):
$("#site1").find("input[checked], input:text, input:hidden, input:password, input:submit, option:selected, textarea")
//.filter(":disabled")
.each(function()
{
$('#site2 [name="'+this.name+'"]').val(this.value);
}
Ok I finally figured this out. It's, more or less, an expansion on Alex Pakka's answer.
sites++;
$("#events").append("<div id='site" + sites + "'>"
+ "<hr><br />"
+ siteform
+ "<center><button class='removesites' title='site"
+ sites + "'>Remove This Site</button><br />");
$("#site1").find("input:checked, input:text, textarea, select").each(function() {
var name = $(this).attr("name");
var val = $(this).val();
var checked = $(this).attr("checked");
var selected = $(this).attr("selectedIndex");
$('#site' + sites + ' [name="'+name+'"]').val(val);
$('#site' + sites + ' [name="'+name+'"]').attr("checked", checked);
$('#site' + sites + ' [name="'+name+'"]').attr("selectedIndex", selected);
});
I used extra vars for readability sake, but it should do just as fine if you didn't and used the methods directly.
Dont forget to create a function for registering the event! Its very important because when the DOM is loaded, all new attributes need to be registrated to the DOM.
Small example:
<script>
$(document).ready(function(){
$('#click-me').click(function(){
registerClickEvent();
})
function registerClickEvent(){
$('<input type="text" name="input_field_example[]">').appendTo('#the-div-you-want')
}
registerClickEvent();
})
</script>
In the process of trying to be more efficient I have been learning a bit of JQuery, but obviously don't know enough.
I need a script that will get each checked function $(':checked').each(function() . Then if a hidden field id (combined with this fields alt tag's) title is not equal to this check boxes title to perform jquery load.
I could go on with trying to explain this but I would rather show what I mean. If you look at the code below the 2 elements which cannot exist are :alt and :title. If you can see what I'm trying to do then any ideas how I would get it working. Data driven site has left me few alternatives.
function product_analysis_global() {
$(':checked').each(function () {
if ($('#product_quantity_PRI_' + ':alt').title != ':title') {
$('#product_' + ':alt').load(':title');
$('#product_quantity_PRI_' + ':alt').title = ':title';
$('#product_quantity_PRI_' + ':alt').value = ':value';
} else if ($('#product_quantity_PRI_' + ':alt').title != 'http://www.divethegap.com/update/blank2.html') {
$('#product_' + ':alt').load('http://www.divethegap.com/update/blank2.html');
$('#product_quantity_PRI_' + ':alt').title = 'http://www.divethegap.com/update/blank2.html';
} else
return false;
});
}
Many Thanks,
ps. Implementation can be seen here where by the radio buttons and checkboxes should load in the appropriate products. You will need to click on Beginners to load the form.
http://www.divethegap.com/update/diving-trips/adventure-training
In the future, please format your code so others can easily read it when asking for help.
Your description isn't exactly clear, and the page you linked to has no radio buttons or checkboxes, but I believe you are looking for:
function product_analysis_global() {
$(':checked').each(function() {
var alt = $(this).attr('alt');
var title = $(this).attr('title');
if ($('#product_quantity_PRI_' + alt).attr('title') != title) {
$('#product_' + alt).load(title);
$('#product_quantity_PRI_' + alt).attr('title', title);
$('#product_quantity_PRI_' + alt).val($(this).val());
} else if ($('#product_quantity_PRI_' + alt).attr('title') != 'http://www.divethegap.com/update/blank2.html') {
$('#product_' + alt).load('http://www.divethegap.com/update/blank2.html');
$('#product_quantity_PRI_' + alt).attr('title', 'http://www.divethegap.com/update/blank2.html');
} else return false ;
});
}
I am a bit lost. On your example page I see no instances of checkboxes or radio buttons? :checked can only be used with these elements. Perhaps you can do a jsfiddle and give a better understanding of what you are trying to achieve?
This is a general question, I have two pages, a main and a backgound function one (file.php)
Main page loads file.php passing variables:
$(document).ready(function() {
var page = $('#page').attr('value');
var user = $('#user').attr('value');
$('#DIV').load('file.php?user=' + user + '&page=' + page);
});
File.php queries database, inserts variables into more jquery stuff..
echos result...
The result on the main page is the desired one. If I fixe the variables in file.php (and load through browser) the script is fully functionnal and interactive.
My problem is as follows:
The file.php part of the main page is not interacive, i.e. when I click on it nothing happens, yet the 2 work fine idependently, together variables are passed, but the result is static.
My question, is this due to the .load() function? Should I be using $.ajax() type GET ...
Thanks in advance.
It sounds like you have jQuery behaviours attached to the DOM that are not being applied to the new content. This is because the new content is loaded after the DOM is ready (ie, after the load event fires).
This can be solved by using the .live jQuery function to attach events to a selector that will be applied to all elements, regardless of when they're added. E.g, instead of:
$('#button').click(function() { alert('hi'); });
Use:
$('#button').live('click', function() { alert('hi'); });
Is #DIV referring to:
<div id="DIV"></div>
Perhaps you should try this:
$(document).ready(function() {
var page = $('#page').attr('value');
var user = $('#user').attr('value');
// see the DIV below with the ID = "myId"
$("#myId").load("file.php?user=' + user + '&page=' + page", function(response, status, xhr) {
if (status == "error") {
var msg = "Sorry but there was an error: ";
$("#error").html(msg + xhr.status + " " + xhr.statusText);
}
});
});
<!-- empty containers with ID attributes -->
<div id="myId"></div>
<div id="error"></div>
If there is an error in the returned data, this will also tell you what the error is. Also, what does your debugger tell you?
For some reason the script below is unable to get the id of the draggable divs using attr('id'), but it works on the other static elements on the page. I am totally confused as to why this wont work and if anyone has a solution for me it would me much appreciated.
$(document).ready(function(){
//$(".draggable").draggable();
$(".draggable").draggable({ containment: '#container', scroll: false });
$(".draggable").draggable({ stack: { group: '#container', min: 1 } });
$("*", document.body).click(function (e) {
var offset = $(this).offset();// get the offsets of the selected div
e.stopPropagation();
var theId = $(this).attr('id');// get the id of the selceted div
$("#result").text(this.tagName + " id=" + theId + " (" + offset.left + "," + offset.top +")");
$.post("http://localhost/index.php", "id=" + theId + "&x=" + offset.left + "&y=" + offset.top); //post x,y to php (and the id of the elemnt)
});
var req = function () {
$.ajax({
url: "out.php",
cache: false,
success: function(html){
$("#stuff").empty().append(html);
var css_attr = html.split(",");
$('#1').css('left', css_attr[0] + 'px').css('top', css_attr[1] + 'px');
},
complete: function(){
req();
}
});
};
req();
});
Note: This script is dependent on the following JavaScript sources.
jquery.js
http://jqueryui.com/latest/ui/ui.core.js
http://jqueryui.com/latest/ui/ui.draggable.js
http://jqueryui.com/latest/ui/ui.droppable.js
Currently you're attaching the click handler to all elements in the DOM with * (very very bad, don't do this!), including any children in those draggables.
You are correctly stopping the event from bubbling up using .stopPropagation(), but it's likely a child of a .draggable you've clicked, not the draggable itself. What you want is actually listening on the .draggable element themselves, like this:
$(".draggable").click(function (e) {
var offset = $(this).offset(),
theId = this.id;
e.stopPropagation();
$("#result").text(this.tagName + " id=" + theId + " (" + offset.left + "," + offset.top +")");
$.post("http://localhost/index.php", { id: theId, x: offset.left, y: offset.top });
});
The other changes here are id can be accessed directly, via this.id, and passing an object to $.post() is safer for serialization, like I have above.
Even the above isn't quite there though, you likely want to send the position when you stop dragging, by changing this:
$(".draggable").click(function (e) {
To this:
$(".draggable").bind("dragstop", function (e) {
...or in newer versions of jQuery (1.4.2+):
$(document.body).delegate(".draggable", "dragstop", function (e) {
Your click function works for me on a test page. Out of curiosity, if you move the 'e.stopPropogation()' line to the bottom of your click function, does it behave differently?
Be careful with *, you know, all means all, if you have <div><p><span><a></a></span></p></div> it means that the action is set to every single element. I'd specify classes or tags that should be affected by your function, to be always sure that you get what you want to be clicked.
Try your code replacing * with the object you think it's ID isn't get, and see if it works..
This may seem pretty obvious but are you sure that all the elements that your selecting actually have IDs. If your including everything (with the *) then it is likely that some elements don't have IDs.