How to select children and self with jQuery? - javascript

I'm wondering what the best way is to find children of an element in jQuery, but also include the parent in the 'find'.
Here's my simplified basic HTML setup:
<div id="container">
<form id="form1"> <!-- form 1 content --> </form>
<form id="form2"> <!-- form 2 content --> </form>
</div>
And I want a function like this...
function getForms ($container)
{
// Option 1
var $allForms = $container.find('form').andSelf().filter('form');
// Option 2
var $allForms = $container.find('form');
if ($container.is('form')) $allForms.add($container);
// Should return all the forms in the container if passed $('#container')
// Or return just one form if passed $('#form1')
return $allForms;
}
I'm fairly certain that both option 1 or 2 will work. Does anyone know which option above is more efficient? Are there other options which are more elegant or more efficient?
EDIT:
I wasn't happy with the .is() in option 2 because it didn't work when the container had multiple jQuery objects in it. So I came up with something like this:
// Option 3
function getContainerElements ($container, selector) {
return $container.find(selector).add($container.filter(selector));
}
I haven't tested it too much, but I think it'll work for all general cases.

Option 2 is better according to JSPerf tests (Tested in Chrome on linux)
You can see the results here and the tests if you want to try it in different browsers:
http://jsperf.com/children-and-self
Note: I've updated this test to use divs instead of forms (because forms inside other forms don't work: Form inside a form, is that alright?) and we want to test the case where the parent should be added to get the real performance impact as per comment below
EDIT
Added third option performance as requested in comment

If you are really only looking for a single element rather than a set of elements, then the following should work at least as well as your option 3 and be faster:
function getContainerElements ($container, selector) {
var filtered = $container.filter(selector);
return filtered.length ? filtered : $container.find(selector);
}
In case there are both descendant and root elements matching selector, this will return only the matching root elements, unlike your option 3, which will put everything together. If you do want that, then modern jQuery allows a simpler solution:
function getContainerElements ($container, selector) {
return $container.find(selector).addBack(selector);
}

Related

Match/group HTML elements by their data attributes values

In the HTML I have multiple elements that are grouped by data attributes, but these attributes don't necessarily have the same parent:
Example:
<span data-toggle-trigger="1">Resources</span>
<p>......<p>
<span data-toggle-trigger="2">Alpha</span>
<div data-toggle-container="1"></div>
<p>......<p>
<span data-toggle-trigger="3">Beta</span>
<div data-toggle-container="2"></div>
<div data-toggle-container="3"></div>
The used attributes are:
data-toggle-trigger` and `data-toggle-container
The behavior I need:
When someone click on data-toggle-trigger something happens on data-toggle-container (like show/hide).
To get them:
triggers = document.querySelectorAll(['data-toggle-trigger']);
container = document.querySelectorAll(['data-toggle-container']);
It looks like I have two options:
To get all of them and try to group them
To get only triggers and than search after that for containers
I'm inclining for 1), but I don't know what the best option is to group.
My naive approach is to think/use 2 for nested loops and loop and compare value, but I don't know if is the best option. So I'm looking for some opinion regarding a good approach.
Important - I'm using pure JavaScript, no ECMAScript 6 syntax sugar (because of other stuff).
This is opinion-based, but I think the most efficient solution is to have only one event listener, possibly on <body>, and if the target has .dataset.toggleTrigger, then show/hide all matching elements, i.e., querySelectorAll('[data-toggle-container="' + x + '"]') in a loop.
I would add classes to manage your triggers and containers. Here is example: Fiddle.
In this snippet I add text every time you click on a trigger.
function myFunction(numberOfTriggerClicked) {
var element = document.querySelectorAll("[data-toggle-container='"+numberOfTriggerClicked+"']")[0];
element.innerText += " NEW TEXT";
}

Toggle on div show/hide

I know there are several questions/solutions to this problem, and I am looking for a combination of two simple solutions(I am not a coder, just a copy/paste/manipulator) so here goes: I need a script which involves a button that toggles show/hide divs on top of a large imagemap, whose function is to turn on/off labels contained within each div. As the imagemap is large and contains groups, I am looking for a way to loop the following functional script:
$(document).ready(function() {
$(".hide").click(function() {
$(this).siblings(".hideShow").hide();
});
$(".show").click(function() {
$(this).siblings(".hideShow").show();
});
});
The loop is needed as I will have well over 200 labels to toggle(removing them will clear some visual space). The html above lists two buttons, whereas I need only one which toggles between the two, like the one behind this jquery bit:
var toggleState = false;
$('.show').click(function() {
$(".text").toggle();
$(this).toggleClass('hide').attr('title', toggleState ? 'Show All' : 'Hide All');
toggleState = !toggleState;
})
The former jfiddle found here: http://jsfiddle.net/MztAm/
The latter here: http://jsfiddle.net/y8ZTj/1/
Actually, as the latter script is more desireable, is it possible to turn it into a loop, replacing ".text" to accomodate many instances?
The best I could come up with: http://jsfiddle.net/qmv3dmya/ though I need a new instant of the jquery piece for every grouping. But I'll end up putting the series in a separate *.js sheet to be referenced by the main page.
It is possible that I don't understand your question correctly, but if I do, then perhaps this information will be helpful:
(1) You do not need a loop. With jQuery, selecting all DIVs with class="text" looks like this:
$('.show').click(function() {
$(".text").toggle();
});
(2) This bit: $(".text") creates an object that contains a list of all DIVs that have class="text"
(3) This bit: .toggle() applies that method to each element contained in $('.text')
Therefore, all elements with class="text" will be toggled visible/invisible as a group. No need for a loop.
jsFiddle Demo
If you did need a loop, you could use .each(), like this:
$('.text').each(function(){
// Whatever you do in here will be done once to each DIV
// (or other element) contained in the object ("list") of
// all elements with `class="text"`
});
You can you siblings function see below.
Jquery:
var toggleState = false;
$('.show').click(function() {
$(this).siblings(".text").toggle();
$(this).toggleClass('hide').attr('title', toggleState ? 'Show All' : 'Hide All');
toggleState = !toggleState;
});
DEMO

How do I make an OnClick function that will change a word when a user clicks it to another word?

Okay so, I want to make an OnClick function in JavaScript that makes it so when a user clicks on it, it will change the word. Is there a replaceword() function or something that which will let me do so? I know this is not real code, but for example:
<p>Quickly <span onclick="replaceword('Surf');">Search</span> The Web!</p>
If there is, then can someone tell me also how to reverse the code maybe? So when they click on it the second time, it will change back to "Search"?
If you want to jump between multiple words, you'll need to store them someplace. You could have two words in the sentence, and toggle the visibility of one or the other (which doesn't scale well), or you could even store them as values on an attribute placed on the element itself.
<p>Hello, <span data-values="World,People,Stack Overflow">World</span>.</p>
I have placed all possible values within the data-values attribute. Each distinct value is separated from the other values by a comma. We'll use this for creating an array of values next:
// Leverage event-delegation via bubbling
document.addEventListener( "click", function toggleWords ( event ) {
// A few variables to help us track important values/references
var target = event.target, values = [], placed;
// If the clicked element has multiple values
if ( target.hasAttribute( "data-values" ) ) {
// Split those values out into an array
values = target.getAttribute( "data-values" ).split( "," );
// Find the location of its current value in the array
// IE9+ (Older versions supported by polyfill: http://goo.gl/uZslmo)
placed = values.indexOf( target.textContent );
// Set its text to be the next value in the array
target.textContent = values[ ++placed % values.length ];
}
});
The results:
The above listens for clicks on the document. There are numerous reasons why this is a good option:
You don't need to wait for the document to finish loading to run this code
This code will work for any elements added asynchronously later in the page life
Rather than setting up one handler for each element, we have one handler for all.
There are some caveats; you may run into a case where the click is prevented from propagating up past a particular parent element. In that case, you would want to add the eventListener closer to your target region, so the likeliness that bubbling will be prevented is less.
There are other benefits to this code as well:
Logic is separated from markup
Scale to any number of values without adjusting your JavaScript
A demo is available for your review online: http://jsfiddle.net/7N5K5/2/
No, there isn't any native function, but you can create on your own.
function replaceword(that, word, oword) {
that.textContent = that.textContent == oword ? word : oword;
}
You can call it like this:
<p>Quickly<span onclick="replaceword(this,'Surf','Search');">Search</span>The Web!</p>
Live Demo: http://jsfiddle.net/t6bvA/6
<p id="abc">Hello</p>
<input type="submit" name="Change" onclick="change()">
function change(){
var ab=document.getElementById('abc').value;
ab.innerHTML="Hi, Bye";
}
I think so this should help you, you should go to site such as w3schools.com, its basic and it will answer your doubt
You can try something like this if you wanna use jQuery
http://jsfiddle.net/R3Ume/2/
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<body>
<p>Hello <a id='name'>John<a></p>
<input id="clickMe" type="button" value="replace" onclick="onClick();" />
<script>
function onClick() {
$('#name').text('world');
}
</script>

Find similar HTML parts

I have following (simplified) HTML code and want to split it into similar parts:
<input id="checkbox1"><label><br>
<input id="checkbox2"><label><br>
<input id="checkbox3"><label><br>
The result for this should be <input><label><br>. But the problem is, I need a bulletproof solution, which would for example return <div><p><input></p><p><label></p></div> from the following HTML:
<div><p><input id="checkbox1"></p><p><label></p></div>
<div><p><input id="checkbox2"></p><p><label></p></div>
<div><p><input id="checkbox3"></p><p><label></p></div>
Any idea how to find such a pseudo parent element in JavaScript/jQuery?
Like Rory McCrossan figured out, this is indeed used for a templates system. The user defines one row in this template, like <input id="checkbox1"><label><br> which is then displayed x times on the screen. I need this template in my JS code, but there is unfortunately no direct access to the user template, so my idea was to figure out which HTML parts look similar and the splitting them to get the template back.
As a partial solution, you could consider identifying the closest common ancestor for an input label pair, and using this as the repeating element:
var collection = $('');
$('input').each(function() {
collection = collection
.add($(this)
.parents(':has(label)')
.filter(function() {
return $(this).siblings().length == $(this).siblings(this.tagName).length;
}));
});
console.log(collection);
This presupposes each label input pair has a common parent element, and so doesn't work for your first case.

How to get a DOM Element from a jQuery selector?

I'm having an impossibly hard time finding out to get the actual DOMElement from a jQuery selector.
Sample Code:
<input type="checkbox" id="bob" />
var checkbox = $("#bob").click(function() { //some code } )
and in another piece of code I'm trying to determine the checked value of the checkbox.
if ( checkbox.eq(0).SomeMethodToGetARealDomElement().checked )
//do something.
And please, I do not want to do:
if ( checkbox.eq(0).is(":checked"))
//do something
That gets me around the checkbox, but other times I've needed the real DOMElement.
You can access the raw DOM element with:
$("table").get(0);
or more simply:
$("table")[0];
There isn't actually a lot you need this for however (in my experience). Take your checkbox example:
$(":checkbox").click(function() {
if ($(this).is(":checked")) {
// do stuff
}
});
is more "jquery'ish" and (imho) more concise. What if you wanted to number them?
$(":checkbox").each(function(i, elem) {
$(elem).data("index", i);
});
$(":checkbox").click(function() {
if ($(this).is(":checked") && $(this).data("index") == 0) {
// do stuff
}
});
Some of these features also help mask differences in browsers too. Some attributes can be different. The classic example is AJAX calls. To do this properly in raw Javascript has about 7 fallback cases for XmlHttpRequest.
Edit: seems I was wrong in assuming you could not get the element. As others have posted here, you can get it with:
$('#element').get(0);
I have verified this actually returns the DOM element that was matched.
I needed to get the element as a string.
jQuery("#bob").get(0).outerHTML;
Which will give you something like:
<input type="text" id="bob" value="hello world" />
...as a string rather than a DOM element.
If you need to interact directly with the DOM element, why not just use document.getElementById since, if you are trying to interact with a specific element you will probably know the id, as assuming that the classname is on only one element or some other option tends to be risky.
But, I tend to agree with the others, that in most cases you should learn to do what you need using what jQuery gives you, as it is very flexible.
UPDATE: Based on a comment:
Here is a post with a nice explanation: http://www.mail-archive.com/jquery-en#googlegroups.com/msg04461.html
$(this).attr("checked") ? $(this).val() : 0
This will return the value if it's checked, or 0 if it's not.
$(this).val() is just reaching into the dom and getting the attribute "value" of the element, whether or not it's checked.

Categories