jQuery click action stripped out by sorting function - javascript

I have a function, including the following code:
$(list).append(item);
$(list).html(
$(list).children("li").sort(function (a, b) {
return $(a).val() - $(b).val();
}));
$(item).click(this.listClick);
(Basically, it is creating a <ul> <li> from an array of items, including a listClick function which fires when any of the list items are clicked, and then sorting the list according to the li value).
If I strip out the sort function from the script, then it works fine (as in, the list is compiled, with click-function working perfectly, albeit not in the correct order). However, if I sort the list, it strips out the click-functionality (i..e. clicking the items doesn't perform the listClick function).
Why is this being stripped out? It doesn't seem to matter whether I place the sort function before or after the listClick line is added.

That's probably because you are using html method which replaces html content of the element and has nothing to do with the sort method. The old elements are removed and their attached event handlers too. You should either use event delegation or better use appendTo instead of html:
$(list).children("li").sort(function (a, b) {
return $(a).text() - $(b).text();
}).appendTo(list);
As the appendTo only moves elements, the event handlers are preserved.
Also note that li element doesn't have value property so .val() method returns an undefined value. You should use text method for getting textContent of the element. And if list is already a jQuery object there is no need to wrap it with jQuery constructor.

Related

Access to object property or method from appended element jquery

I would like to know if it's possible to access an object property from an appended element. For example:
function anyFct(){
this.div=$('<div ref="dv">').html('Hi').appendTo('body');
div.animal='dog';
div.yld=function(){
alert(div.animal);
};
$('input type="text" value="anyIn" onclick="yeldAnimal(this);"').appendTo(div);
}
function yeldAnimal(obj){
var actElement=$(obj).closest('div[ref=dv]');
actElement.yld(); // I want that this yields 'dog'
}
and my HTML:
<input type="button" value="test" onclick="anyFct();">
So this is the logic: I create a div element when the button is clicked on. This div element has a text that when clicked on calls an external function that calls a method on its parent element (the div).
For many contextual reasons this must be the logic. I've already found a solution that is saving the object div in a global array and then search in all values of the array for the object that triggered the method. However, I would like to know if there is a 'cleaner' or correct way to do this.
It's possible, and there are a couple of ways you could achieve it. The important thing you need to understand is the distinction between jQuery objects and actual DOM elements. When you use jQuery to create a <div> element, you create both; but what you end up with a reference to is the jQuery object - or, if you're chaining jQuery function calls, the result of the last function called. The DOM element, assuming you actually append it to the DOM, persists once that section of code has finished execution, but the jQuery object that's created will vanish when that variable goes out of scope.
When you execute some jQuery code later on to get a reference to your DOM element, it's referring to the same element on your page but it's a different jQuery object, so any custom properties you added to the original one won't be available. How do you get around that? Set the properties on the actual DOM element.
You can use the .get() method to access the underlying DOM element from a jQuery object, indexed from 0 (so .get(0) called on a jQuery object will return the first DOM element it references). With that you can then set your custom properties and later retrieve them, something like this:
function anyFct(){
this.div=$('<div ref="dv">').html('Hi').appendTo('body');
var elem = div.get(0); // the actual DOM element, the div
elem.animal='dog';
elem.yld=function(){
alert(elem.animal);
};
$('<input type="text" value="anyIn" onclick="yeldAnimal(this);"/>').appendTo(div);
}
function yeldAnimal(obj){
var actElement=$(obj).closest('div[ref=dv]').get(0); // also the div
actElement.yld(); // alerts 'dog'
}
jsFiddle demo
Note that I've made a few changes to your code in addition to adding in the usage of .get(), most notably correcting the syntax for creating the <input type="text"> element in the first function.
Okay, most of this is not syntactically correct javascript and seems to be overly complicated. I believe if I understand what you're trying to achieve you want the following:
function anyFct(){
var div=$('<div ref="dv">').html('Hi');
div.animal='dog';
div.yld=function(){
alert(this.animal);
};
var element = $('<input type="text" value="anyIn">');
$(element).click(function() {
div.yld();
});
$(div).append(element);
$('body').append(div);
}

KnockoutJS / jQuery UI Sortable Conflict

I'm attempting to use KnockoutJS and jQuery UI Sortable together. I know this has been done before (particularly, knockout-sortable), but my use case has some pretty specific behavior and I'm hoping to avoid trying to make the switch.
Anyway, the problem is pretty straightforward - after moving a DOM element with jQuery UI Sortable, Knockout behaves strangely when removing the observableArray element bound to that DOM element. It will fail to remove the moved element, and if the element that fell into the moved element's place is removed, it will remove both that and the originally moved element. Hard to put into words, but demonstrated by this fiddle.
The problem seems actually take place in the following block in knockout-2.1.0.js:
function fixUpVirtualElements(contiguousNodeArray) {
// Ensures that contiguousNodeArray really *is* an array of contiguous siblings, even if some of the interior
// ones have changed since your array was first built (e.g., because your array contains virtual elements, and
// their virtual children changed when binding was applied to them).
// This is needed so that we can reliably remove or update the nodes corresponding to a given array item
if (contiguousNodeArray.length > 2) {
// Build up the actual new contiguous node set
var current = contiguousNodeArray[0], last = contiguousNodeArray[contiguousNodeArray.length - 1], newContiguousSet = [current];
while (current !== last) {
current = current.nextSibling;
if (!current) // Won't happen, except if the developer has manually removed some DOM elements (then we're in an undefined scenario)
return;
newContiguousSet.push(current);
}
// ... then mutate the input array to match this.
// (The following line replaces the contents of contiguousNodeArray with newContiguousSet)
Array.prototype.splice.apply(contiguousNodeArray, [0, contiguousNodeArray.length].concat(newContiguousSet));
}
}
This call is adding the moved DOM element to the list of elements to be removed when the shifted element is removed.
So an open call to any jQuery UI / Knockoutjs geniuses - is there a way to resolve this conflict, or do I need to do something entirely different to make these tools play nicely together?
I think the "best" solution is to remove the element from DOM and change its position in KO. You can do this in the stop event of the sortable. http://jsfiddle.net/vgrTY/4/
I went ahead and changed your array-contents text to a computed as well so it'll properly display.

adapting a click-to-insert by substituting alternate text

I am trying to adapt this script: jQuery Example: Inserting text with drag n’ drop.
The goal is for my users to click an image thumbnail and have the appropriate markdown image code inserted into the textarea.
I am not familiar with JS, so I am aware that the meat of the script is happening here:
$('#ClickWordList li').click(function() {
$("#txtMessage").insertAtCaret($(this).text());
return false
});
specifically, the .text() bit there, but do not know how to alter the output to suit my needs for a snippet of markdown being inserted rather than just, say, the list text.
Markdown inline image syntax looks like this: ![Alt text](/path/to/img.jpg)
I tried changing the list to a div with images and then changing to .insertAtCaret($(this).src); but I get "undefined" as the insertion text.
$(this).src is undefined because $(this) is a jQuery object, and jQuery objects don't have a src property. Assuming you have an image element in your selector, You can try:
$("#txtMessage").insertAtCaret($(this).attr('src'));
The .attr() jQuery method, when given only one parameter, will return the matched element's given attribute's value.
or
$("#txtMessage").insertAtCaret($(this)[0].src);
To get the literal DOM element's src.
Extra explanations (if you need)
The .text() jQuery method, when given no parameters, will return all text inside of the element referenced by this, that is, the event's target, in this case the clicked li inside the #ClickWordList element.
.insertAtCaret() is an extended function of the jQuery object provided by your drag 'n drop plugin.
return false will do exactly what it says, returning false in the click event's handler for the selected element, which simply cancels the event and prevents its propagation from bubbling up the DOM further.
Sorry if there are any typos.

Some questions about how jquery selectors traverse the dom

How do I know what traverses the DOM and what doesn't?
$('div p')
It seems like this returns all the div elements AND THEN does another scan for P elements on each dom element that was returned in the first div search.
$('div .foo')
The class ones don't seem to scan the dom. They only filter the previous list $('div') for elements that contain classes foo. If a child of $('div') has class foo it is not selected.
$('div, div')
Doesn't contain dupes. So it seems to be scanning only once with a list of lambdas that either compare or they don't. But this gets really really confusing when you have filters like :contains('x') which seem like they can recurse the dom on their very own.
So how do these selectors work? Does 'div .foo' traverse for only divs first and then do a filter for classes that contain foo, or does it somehow get turned into a computation that says when tag==Div && class==foo. What about when there's multiple selectors? They show up in the order they appeared on the page without dupes making me feel like it only scanned the dom once. Maybe it just sorts and removes dupes before returning?
jQuery optimises it's selectors based on what is quickest. If there is a native browser supported method for getting an element (getElementById etc) it will use it, otherwise it will filter based on the results of the natively supported methods.

jQuery each() behavior if changing DOM within loop

What is the behavior of jquery when using each() loop? Is it:
look for the first object, execute function, look for the next object...
gather all objects in a container, then execute the function on each of them
other (what exactly?)
An example, where it is relevant:
<div id="a">
A
<div id="b">
B
</div>
</div>
<div id="c">
C
</div>
If I execute this javascript:
$('div').each(function(index){
alert($(this).html());
$(this).remove();
}
Will I see three alerts or only two?
Regardless of what action you will perform on a selection, jQuery will make that selection before applying that action. By which I mean $('div') is a selector, the selection process happens before and regardless of the other chained methods (such as each()). This is a product of the language, since $() must be evaluated before a method can be called upon it.
If that selection grabbed three divs from your page, then there are now 3 jQuery objects in a list ready to be iterated over. You can prove this by doing:
$('div').length
Thus you are iterating over an array with three indexes (0, 1, 2), if you remove the div from the DOM for index 1, the next iteration of the each() callback will still be for the object at index 2. Checkout this live demo for proof:
DEMO: http://jsfiddle.net/marcuswhybrow/HYJa4/
jQuery will behave as you've described in #2. You'll see three alerts:
"A <div> B </div>"
"B"
"C"
However, since div B is inside div A, it will be removed with div A. jQuery still knows about it, and will alert its contents, but it will have already been removed by that time. So, although you get three alerts, you only have two removes (since it removes child elements).
2 is the answer.
jQuery gathers a list of matching elements, then loops over the list.
One important think though, do not remove any element that can be mached that has not already been processed or you will risk getting a reference not found error.

Categories

Resources