jquery - mix variable referencing a dom element with selector - javascript

I usually create variables for frequently reused DOM elements like this:
var $dom_element = $('#dom_element);
where this is my setup:
<div id="dom_element">
<div class="child_element">
<div class="child_element">
</div>
what I'm wondering is if I can mix this variable with a subselector to get child elements. I guess it would be something like this:
var $child_element = $($dom_element + ' .child_element);
And if so, is there any speed benefit to doing this versus just saying:
$('.child_element);
considering the fact that both of these elements might be deeply nested in a large site?

With
var $dom_element = $('#dom_element);
I would use the following to get the child elements
var $child_element = $dom_element.find(".child_element");//I prefer this one, it is easier to read.
or
var $child_element = $(".child_element", $dom_element);
From my research/reading, it appears that setting an element to a variable is best if you are going to reference it many times. That way jQuery does not have to search the DOM many times.

Regarding your selectors, you can get yourself into trouble when using a bare class as a child selector. What if you have multiple child nodes using that same class?
.find() works, as others have suggested. You could alternatively use .children():
var $kids = $dom_element.children('.child_element');
http://api.jquery.com/children/
The difference between .find() and .children() is that .children() will only look one level down the DOM tree. .find() will recursively run through all possible child nodes to match your selector.

Related

How do you grab an element relative to an Element instance with a selector?

I am writing a small library where I am in need of selecting a relative element to the targeted element through querySelector method.
For example:
HTML
<div class="target"></div>
<div class="relative"></div>
<!-- querySelector will select only this .target element -->
<div class="target"></div>
<div class="relative"></div>
<div class="target"></div>
<div class="relative"></div>
JavaScript
var target = document.querySelectorAll('.target')[1];
// Something like this which doesn't work actually
var relativeElement = target.querySelector('this + .relative');
In the above example, I am trying to select the .relative class element relative only to the .target element whose value is stored in target variable. No styles should apply to the other .relative class elements.
PS: the selectors can vary. So, I can't use JavaScript's predefined methods like previousElementSibling or nextElementSibling.
I don't need solution in jQuery or other JavaScript libraries.
Well it should be ideally:
var relativeElement = target.querySelector('.relative');
But this will actually try to select something inside the target element.
therefore this would only work if your html structure is something like:
<div class="target">
<div class="relative"></div>
</div>
Your best bet would probably in this case be to use nextElementSibling which I understand is difficult for you to use.
You cannot.
If you insist on using the querySelector of the subject element, the answers is there is no way.
The spec and MDN both says clearly that Element.querySelector must return "a descendant of the element on which it is invoked", and the object element you want does not meet this limitation.
You must go up and use other elements, e.g. document.querySelector, if you want to break out.
You can always override Element.prototype.querySelector to do your biddings, including implementing your own CSS engine that select whatever element you want in whatever syntax you want.
I didn't mention this because you will be breaking the assumption of a very important function, easily breaking other libraries and even normal code, or at best slowing them down.
target.querySelector('.relative');
By using querySelector on the target instead of document, you scope the DOM traversal to the target element.
It is not entirely clear from your explanation, but by related i assume you mean descendant?
To get all target elements you can use
document.querySelectorAll('.target')
And then iterate the result
I found a way which will work for my library.
I will replace "this " in the querySelector with a unique custom attribute value. Something like this:
Element.prototype.customQuerySelector = function(selector){
// Adding a custom attribute to refer for selector
this.setAttribute('data-unique-id', '1');
// Replace "this " string with custom attribute's value
// You can also add a unique class name instead of adding custom attribute
selector = selector.replace("this ", '[data-unique-id="1"] ');
// Get the relative element
var relativeElement = document.querySelector(selector);
// After getting the relative element, the added custom attribute is useless
// So, remove it
this.removeAttribute('data-unique-id');
// return the fetched element
return relativeElement;
}
var element = document.querySelectorAll('.target')[1];
var targetElement = element.customQuerySelector('this + .relative');
// Now, do anything with the fetched relative element
targetElement.style.color = "red";
Working Fiddle

jquery selector efficiently

<div id="parent">
<div id="child"></div>
</div>
<!-- script -->
<script type="text/javascript">
var $parent = $("#parent");
//$parent().dosomething()
// ... use $parent do something
//!!! now i want use child !!!
var $child = $parent.find("#child"); //method one
var $child = $("#child"); //method two
//$child().dosomething()
</script>
Question: method one or method two is better ??(hope explanation in detail ,thanks!)
waiting for your help
here is a breakdown of what would be fastest. It shows that the $parent.find method is about 50% slower than the direct selector method. That means that if you've got to do with ids at all, it's quite significantly faster to just use the selector on its own.
As elclanrs said, as each ID must be unique, it's more efficient to call directly the ID selector (method two) than calling the find() function. Moreover, the find() function will get all the descendants of each element in the current set of matched elements, filtered by a selector (in this case it wouldn't be a good idea cause you are requesting just one element, filtered by its ID, which once again, must be unique). Method one will be useful just if you need to get more than one element inside the parent div, like <li> selectors, elements of the same class, etc.

jQuery object where properties are DOM elements

I'm looking for a more succint and possibly more jQuery-e way of doing the following.
I have an object lbl which is a div. Contained within the div is a span tag that is the firstName and lastName property of the lbl object. So, so far we have the following HTML:
<div class="label">
<span class="firstName">John</span>
<span class="lastName">Doe</span>
</div>
Now consider the following fiddle http://jsfiddle.net/VfErV/
$('#btnSubmit').click(function ()
{
var lbl = $('<div>').addClass('label');
//I need to be able to access the child elements of the parent div
//via the dot operator
firstName = $('<span>').text('test1').addClass('firstName');
lastName = $('<span>').text('test2').addClass('lastName');
lbl.append(firstName);
lbl.append(lastName);
lbl.firstName = firstName;
lbl.lastName = lastName;
$('#page').append(lbl);
console.log(lbl.firstName.text());
//span tag is never added to the div
var lbl2 = $('<div>').addClass('label');
lbl2.firstName = $('<span>').text('test2').addClass('firstName');
$('#page').append(lbl2);
});
Please refer to the fiddle for behavior. I need to be able to control font-sizes, borders, etc. on the child objects of the parent div container, lbl, and have the appear on the page as normal DOM elements. All of the JS up until the first lbl control is appended to the page works. This looks clunky to me, though. Is there a better way to have child elements of a DOM element be properties of the parent object more easily/succintly?
You asked for a more jQuery-way to do this. Here's how you could create your structure:
$('#btnSubmit').click(function () {
var lbl = $('<div>').addClass('label');
lbl.html('<span class="firstName">test1</span><span class="lastName">test2</span>');
$('#page').append(lbl);
// Then, to retrieve that data, you can just do something like this
console.log(lbl.find(".firstName").text());
});
Then, anytime you want to get the firstName from a given label div, you just use .find("firstName") to get that object and then get its text.
Best practices generally just use the structure of the DOM and find things as needed in the DOM rather than make new duplicate ways to access everything. For 99% of what we do, accessing things through the DOM is plenty fast and doesn't require creation and duplication of another way to get to the data. And, jQuery makes it pretty easy to find things in the DOM as long as you have enough id or class markers to identify what you're looking for (which you do).

reach a HTML element using javascript without getElementById

I'm new to javascript. I created this div called colorme. I can successfully color it via javascript. Now assuming i want to change the background of <p>...</p>, or <span>,etc how do i reach it via Javascript? (no jquery).
Like document.getElementById() would work on the div and i reach it. Now i cannot keep giving unique id's to all the elements. How do i reach the inner elements like <p> or <span>, etc?
<div id="colorme">
<p>Blah Vblah Blah Content</p>
<span>Blah Vblah Blah Content</span>
</div>
You can use the element that you've found as a context for getElementsByTagName.
var colorme = document.getElementById('colorme'),
spans = colorme.getElementsByTagName('span');
Note that spans is a NodeList -- similar to an array -- containing all the span elements within colorme. If you want the first one (indeed, the only one in your code sample), use spans[0].
You should check out the many DOM traversal functions provided in standard javascript.
Tutorial: http://www.quirksmode.org/dom/intro.html
Reference: http://reference.sitepoint.com/javascript/Node
and http://reference.sitepoint.com/javascript/Element
Although the answers do give good ways to do it for this specific case....
The issue you're facing is called DOM-traversal. As you know, the DOM is a tree, and you can actually traverse the tree without knowing in advance the element id/type/whatever.
The basics are as follows
el.childNodes to access a list of children
el.parentNode to access the parent element
nextSibling and previousSibling for next and previous sibling nodes
For further info, see [MDC DOM pages](
Here are three ways:
If you only care about decent browsers, document.querySelector (returns the first matching node) and document.querySelectorAll (returns a NodeList) - e.g. document.querySelector('#colorme p').
HTMLElement.getElementsByTagName() (returns a NodeList) - e.g. document.getElementById('colorme').getElementsByTagName('p')[0]
HTMLElement.children, etc. - document.getElementById('colorme').children[0] (.firstChild will probably be a text node, lots of fun DOM stuff to get into there, the quirksmode DOM intro linked to is good stuff).
It's quite simple: getElementsByTagName()?
You could use getElementsByTagName()
Loop through the children:
var div = document.getElementById('colorme');
var i, l, elem;
for (i = 0, l = div.childNodes.length; i < l; i++) {
elem = div.childNodes[i];
// Check that this node is an element
if (elem.nodeType === 1) {
elem.style.color = randomColorGenerator();
}
}
In this case you can use:
var colormeDiv = document.getElementById('colorme');
var e1 = colormeDiv.getElementsByTagName('p');
var e2 = colormeDiv.getElementsByTagName('span');
to get the two elements inside 'colorme' div.
getElementById is just one of JavaScript's DOM methods. It returns an HTMLElement DOM object which you can then query to find child, parent and sibling elements. You could use this to traverse your HTML and find the elements you need. Here's a reference for the JavaScript DOM HTMLObject.
[after answering, I realised this is no answer to your fully explained question, but it is the answer to the question raised in the title of your post!]
One nice way of doing this is declaring a global var on the top of your Javascript that refers to the document, which can then be used everywhere (in every function):
<html>
<head>
<script type="text/javascript">
// set a global var to acces the elements in the HTML document
var doc = this;
function testIt()
{
doc.blaat.innerHTML = 'It works!!';
}
</script>
</head>
<body>
<div id="blaat">Will this ever work..?!</div>
<button onclick="testIt()">Click me and you'll see!</button>
</body>
</html>
As my first impression when I got to 'getElemenyById()' was that it sounds like a function that will iterate through the DOM's element list until it finds the element you need; this must take some time. With the above example, you simply access the element directly.
I'm not sure if I'm really saving CPU / adding speed this way, but at least it feels that way :)

How to remove only the parent element and not its child elements in JavaScript?

Let's say:
<div>
pre text
<div class="remove-just-this">
<p>child foo</p>
<p>child bar</p>
nested text
</div>
post text
</div>
to this:
<div>
pre text
<p>child foo</p>
<p>child bar</p>
nested text
post text
</div>
I've been figuring out using Mootools, jQuery and even (raw) JavaScript, but couldn't get the idea how to do this.
Using jQuery you can do this:
var cnt = $(".remove-just-this").contents();
$(".remove-just-this").replaceWith(cnt);
Quick links to the documentation:
contents( ) : jQuery
replaceWith( content : [String | Element | jQuery] ) : jQuery
The library-independent method is to insert all child nodes of the element to be removed before itself (which implicitly removes them from their old position), before you remove it:
while (nodeToBeRemoved.firstChild)
{
nodeToBeRemoved.parentNode.insertBefore(nodeToBeRemoved.firstChild,
nodeToBeRemoved);
}
nodeToBeRemoved.parentNode.removeChild(nodeToBeRemoved);
This will move all child nodes to the correct place in the right order.
You should make sure to do this with the DOM, not innerHTML (and if using the jQuery solution provided by jk, make sure that it moves the DOM nodes rather than using innerHTML internally), in order to preserve things like event handlers.
My answer is a lot like insin's, but will perform better for large structures (appending each node separately can be taxing on redraws where CSS has to be reapplied for each appendChild; with a DocumentFragment, this only occurs once as it is not made visible until after its children are all appended and it is added to the document).
var fragment = document.createDocumentFragment();
while(element.firstChild) {
fragment.appendChild(element.firstChild);
}
element.parentNode.replaceChild(fragment, element);
$('.remove-just-this > *').unwrap()
More elegant way is
$('.remove-just-this').contents().unwrap();
Use modern JS!
const node = document.getElementsByClassName('.remove-just-this')[0];
node.replaceWith(...node.childNodes); // or node.children, if you don't want textNodes
oldNode.replaceWith(newNode) is valid ES5
...array is the spread operator, passing each array element as a parameter
Replace div with its contents:
const wrapper = document.querySelector('.remove-just-this');
wrapper.outerHTML = wrapper.innerHTML;
<div>
pre text
<div class="remove-just-this">
<p>child foo</p>
<p>child bar</p>
nested text
</div>
post text
</div>
Whichever library you are using you have to clone the inner div before removing the outer div from the DOM. Then you have to add the cloned inner div to the place in the DOM where the outer div was. So the steps are:
Save a reference to the outer div's parent in a variable
Copy the inner div to another variable. This can be done in a quick and dirty way by saving the innerHTML of the inner div to a variable or you can copy the inner tree recursively node by node.
Call removeChild on the outer div's parent with the outer div as the argument.
Insert the copied inner content to the outer div's parent in the correct position.
Some libraries will do some or all of this for you but something like the above will be going on under the hood.
And, since you tried in mootools as well, here's the solution in mootools.
var children = $('remove-just-this').getChildren();
children.replaces($('remove-just-this');
Note that's totally untested, but I have worked with mootools before and it should work.
http://mootools.net/docs/Element/Element#Element:getChildren
http://mootools.net/docs/Element/Element#Element:replaces
I was looking for the best answer performance-wise while working on an important DOM.
eyelidlessness's answer was pointing out that using javascript the performances would be best.
I've made the following execution time tests on 5,000 lines and 400,000 characters with a complexe DOM composition inside the section to remove. I'm using an ID instead of a class for convenient reason when using javascript.
Using $.unwrap()
$('#remove-just-this').contents().unwrap();
201.237ms
Using $.replaceWith()
var cnt = $("#remove-just-this").contents();
$("#remove-just-this").replaceWith(cnt);
156.983ms
Using DocumentFragment in javascript
var element = document.getElementById('remove-just-this');
var fragment = document.createDocumentFragment();
while(element.firstChild) {
fragment.appendChild(element.firstChild);
}
element.parentNode.replaceChild(fragment, element);
147.211ms
Conclusion
Performance-wise, even on a relatively big DOM structure, the difference between using jQuery and javascript is not huge. Surprisingly $.unwrap() is most costly than $.replaceWith().
The tests have been done with jQuery 1.12.4.
if you'd like to do this same thing in pyjamas, here's how it's done. it works great (thank you to eyelidness). i've been able to make a proper rich text editor which properly does styles without messing up, thanks to this.
def remove_node(doc, element):
""" removes a specific node, adding its children in its place
"""
fragment = doc.createDocumentFragment()
while element.firstChild:
fragment.appendChild(element.firstChild)
parent = element.parentNode
parent.insertBefore(fragment, element)
parent.removeChild(element)
If you are dealing with multiple rows, as it was in my use case you are probably better off with something along these lines:
$(".card_row").each(function(){
var cnt = $(this).contents();
$(this).replaceWith(cnt);
});
The solution with replaceWith only works when there is one matching element.
When there are more matching elements use this:
$(".remove-just-this").contents().unwrap();

Categories

Resources