I've just started using jQuery, and can't get my head around this.
I need to check if a div contains anything but a h3 header tag.
The structure of the html is as follows:
<div id="myContainer">
<h3>A header that is always present</h3>
any html, plain text or empty
</div>
Here's the jQuery:
if ( $("#myContainer h3:only-child") ) {
$("#myContainer").css("display", "none");
}
The above will simply hide the div if there is no other html elements present, which is not what I intend since it will also contain plain text. I have tried with other selectors and functions, but I just can't seem to get it right.
Thanks in advance :)
I think you want a function which will tell you if the div contains just H3 or also has other elements. I don't think there is a direct jQuery selector for that, so I wrote a simple function to checkForJustH3.
DEMO - That shows how to hide div that just has H3.
Check out the below demo by editing the div contents and Hit Run to see the result.
DEMO
function containsJustH3 (el) {
var result = true;
$(el).contents().each (function() {
if (this.nodeName != 'H3') {
//check for blank line or empty spaces
if (this.nodeType == 3 && $.trim(this.nodeValue) == '') {
return true;
}
result = false;
return false;
}
});
return result;
}
And using the above function you can do something like,
if ( containsJustH3("#myContainer") ) {
$("#myContainer").css("display", "none");
}
Try:
$("#myContainer").children('h3').hide();
jsfiddle
You can wrap the 'plain text' in a p element and use this selector:
fiddle
$("#myContainer").children().not('h3').hide();
//Select all children but not the h3
HTML:
<div id="myContainer">
<h3>A header that is always present</h3>
<p>any html, plain text or empty</p>
</div>
You can use the length property to see how many children the div has
$("#myContainer").children('h3').length
The fundamental problem here is that jQuery methods like children will not get text nodes. If you want to see how many children a div has, and include the text nodes, you'll want to grab the actual dom node, and check the length of its childNodes property. This will include text nodes.
if ($("#myContainer")[0].childNodes.length > 1) {
var $cont=$('#myContainer'), $children=$cont.contents().not('h3');
if( ! $children.length) {
$cont.hide();
}
Using contents() method will check if there are tags or text nodes in element, check if the children object has length, if not hide the container
You can use $("#myContainer").contents().length to get the number of nodes, including text nodes. However, this includes the newline before the <h3>, etc.
Related
i have a simple question about JQuery...
I have a main <div> containing some divs, which have dynamical ids. each child div has a <h4> tag in it. These h4 does not have any id, nor class, nor name. They just have some text in it.
Here is what i'm talking about :
<div id="main_div">
<div id ="div01">
<h4>Sometext01</h4>
</div>
<div id ="div02">
<h4>Sometext02</h4>
</div>
<div id ="div03">
<h4>Special Text!!!</h4>
</div>
<div id ="div04">
<h4>Sometext04</h4>
</div>
</div>
I would like to get the parent div's id of the h4 which text is "Special text!!!" (so, here, in this example, it is div03).
But, unfortunately, these ids are auto generated (by Sugarcrm, for those who know), and i can't know by advance the id's name. So that is why i have to pass by the h4.
I know i can use the jQuery function below to pass through all h4
allMyH4.each()
And i know i can use the jQuery function below to find the parent of my desired h4
myFoundH4.parent()
But i'm getting some trouble on searching the h4 by their text() (or html() )
Could you please help me on that?
Thanks a lot
EDIT few hours later :
Thanks to #Bhushan , i could do what i really wanted. It just has a restriction : my user MUST not change h4 texts NOR giving several h4 the same name containing my string.
For another solution, try #rory too, it did not work for me, but it had been tested by others, and it seems to be better than contains.
You can use filter():
var $h4 = $('h4').filter(function() {
return $(this).text() == 'Special Text!!!';
});
console.log($h4.parent().prop('id'));
This will ensure an exact match on the text property, not a partial match as the :contains selector will give. filter should also be faster.
Try this :
$(function(){
var parentId = $('main_div').find('h4:contains("Special Text!!!")').parent().attr('id');
});
Have you tried :contains()?
var divId = $("h4:contains('Special Text!!!')").parent().attr('id');
If there's other <h4> tags containing text which includes your Special Text!!!, but you don't want to select them, you can use .filter() as #Rory's answer says:
var divId = $("h4:contains('Special Text!!!')").filter(function(i, elt) {
return $(elt).text() === 'Special Text!!!';
}).parent().attr('id');
However if you have multiple <h4>s with this text, it could still match multiple elements.
$("h4").each(function(inx, elem){
var pattern = "Special Text!!!", el = $(elem);
if(el.text() == pattern){
console.info(el.parent().attr("id"));
}
});
Update: Roy McCrossan has a good solution.
Try this:
FIDDLE
$("h4").each(function(){
if($(this).text() === 'Special Text!!!')
{
var pid = $(this).parents().attr("id");
alert(pid);
}
});
I have the following HTML :
<div id="rightCon">
</div>
And then I have the following script at the top :
$('#rightCon:empty').hide();
Why is the div not hiding? I can see that there is some spaces(that I can´t get ridoff) but does that really matter? How do I remove/hide this div when its empty with only spaces?
Your element appears to have a bunch of white space, which will give it a text node. It won't be matched by :empty for that reason.
You could try finding the element and checking it's contents explicitly:
$('#rightCon').filter(function() {
var text = $(this).text().replace(/\s*/g, '');
return !text;
}).hide();
This solved the problem.
$(document).ready(function () {
if($('#rightCon').text().trim().length < 1)
{$('#rightCon').hide();}
});
Your div is not actually empty (It contains whitespace). So the selector $("#rightCon:empty") will never evaluate and hide the div.
Since HTML elements should be unique, you can safely assume that you can select the correct element via:
var rightCon = $("#rightCon");
You can then hide the element via:
right.hide();
Or
$("#rightCon").hide();
Why the following code not work as expected? (Select all elements before the div with class = clear?)
HTML:
<div id="text">
line0
<div>line1</div>
<div>line2</div>
<div class="clear" />
<div>dummy</div>
<p>dummy</p>
</div>
JS:
var allow = true;
var output = $('#text *').filter(function(index) {
if( $(this).attr("class") == 'clear') {
allow = false;
}
return allow;
}).html().trim();
alert( output );
Output:
line1
Expect:
line0
line1
line2
This happens because as the .html() says
Get the HTML contents of the first element in the set of matched
elements.
So though both of your div is selected only one is returned by .html()
Check this fiddle to verify you code is returning both elements.
And as other have already said you should use the .hasClass method.
UPDATE
As that line0 is not inside any node, its a textNode, You can loop and add span tags around textNodes first. Or you wont be able to apply styling to that text. Check the following code
var whitespace = /^\s*$/;
$('#text').contents().filter(function(){
return this.nodeType == 3 && !whitespace.test(this.nodeValue);
}).wrap('<span></span>');
This loops through all childNodes(including textnodes) and wrap non-space textnodes with span.
So after that your line0 will be inside a span tag like <span>line0</span>. So now if you do
$('.clear').prevAll().css('color', 'red');
It will highlight line0 too.
Check Node Types Doc on MDN
Working Fiddle
Because the html() method returns the contents of the first element in the selector.
http://api.jquery.com/html/
If the selector expression matches more than one element, only the first match will have its HTML content returned.
$('.clear').prevAll().css('color', 'red').fadeOut(2000);
Live DEMO
And by the way, if you want to check if an element has a class use .hasClass(class)
Example:
var hasClass = $(this).hasClass('clear');
What is the best way in your experience to detect the existence of an element inside a div?
I'm currently using this:
if (($('.parentDiv').width()) > 0){
//Do something
}
There has to be a more elegant way.
If empty means not even text nodes:
if ($('.parentDiv').contents().length){
//Do stuff
}
or:
$('.parentDiv:not(:empty)') // which is nice...
.contents docs:
Description: Get the children of each element in the set of matched elements, including text and comment nodes.
if you care only of elements and not text nodes:
if ($('.parentDiv').children().length){
//Do stuff
}
Probably not what you want, but considering there's at least a little confusing over your requirements, you could consider if anything at all is within the container: elements, text, whatever.
Say you have an empty div:
<div class="parentDiv"></div>
Then $(".parentDiv").html().length == 0 indicates its emptiness.
If the div is not empty:
<div class="parentDiv"> </div>
<div class="parentDiv"><div></div></div>
Then $(".parentDiv").html().length will indicate its occupiedness (returning 1 and 11, respectively, in those scenarios.)
If you wish to check only for elements, or specific elements, then $(".parentDiv").children() would be the way to go.
Assuming you only care about elements (and not text nodes), you could check to see if the element has any children:
if($('.parentDiv').children().length) {
//Do stuff
}
Use the children() function
$('.parentDiv').children()
if ( $( '.parentDiv' ).children().length > 0 )
{
// do something
}
I have a div set up something like this:
<div id="test"> <p>Hello</p> <p style="display: none">Goodbye</p> </div>
EDIT: To clarify, this is the simplest example. The div could have any arbitrary number of n deep nested children.
$('#test').getText() returns 'Hello Goodbye'. Here's a one liner to test in Firebug: jQuery('<div id="test"> <p>Hello</p> <p style="display: none">Goodbye</p> </div>').text()
This seems to be because what jQuery uses internally, textContent (for non IE), returns hidden elements as part of the text. Hrmph.
Is there a way to return the text content ignoring display:none'd elements? Basically I am trying to mimic the text you would get from highlighting the div with your mouse and copying to system clipboard. That ignores hidden text.
Interestingly, if you create a selection range and get the text from it, that also returns text inside display:none elements.
var range = document.body.createTextRange();
range.moveToElementText($('#test')[0]);
range.select();
console.log(range.toString()); // Also logs Hello Goodbye!
So creating a document selection range doesn't appear to do the same thing as highlighting with the mouse in terms of display:none elements. How do I get around this dirty pickle conundrum?
Edit: using .filter(':visible').text has been suggested, but it won't work for this scenario. I need the returned text to be EXACTLY what would come from a selection with the mouse. So for example:
$('<div>test1 <p>test2</p>\r\n <b>test3</b> <span style="display:none">none</span></div>').appendTo(document.body).children().filter(':visible').text()
returns
"test2test3"
When the output I actually want is
test1 test2
test3
linebreaks, whitespace and all, which come from the \r\n
Filter the elements using .filter(":visible").
Or use this:
$("#test :visible").text();
But the jQuery documentation advises us to use .filter() instead:
Because :visible is a jQuery extension and not part of the CSS specification,
queries using :visible cannot take advantage of the performance boost provided by the native DOM querySelectorAll() method. To achieve the best performance when using :visible to select elements, first select the elements using a pure CSS selector, then use .filter(":visible").
Use :visible in your selector as such:
$("#test > p:visible").text()
A Function example:
-- Edit:
http://jsfiddle.net/8H5ka/ ( Works on Chrome it displays "Hello" in Result )
If the above doesn't work:
http://jsfiddle.net/userdude/8H5ka/1/
If space isn't a major concern you could copy the markup, remove the hidden elements, and output that text.
var x = $('#test').clone();
x.filter(':not(:visible)').remove();
return x.text();
I had this problem and found this question, and it seems the actual solution is based on the provided answers but not actually written out. So here's a complete solution that worked for my situation, which is the same as the OP with the additional provision that elements may be invisible due to external styles based on DOM position. Example:
<style>.invisible-children span { display: none; }</style>
<div class="invisible-children">
<div id="test">Hello <span>Goodbye</span></div>
</div>
The solution is to:
Make a clone of the entire object.
Remove invisible objects in place; if we take #test out of the DOM before we remove invisible objects, jQuery might not know they're invisible because they will no longer match the CSS rules.
Get the text of the object.
Replace the original object with the clone we made.
The code:
var $test = $('#test');
// 1:
var $testclone = $test.clone();
// 2: We assume that $test is :visible and only remove children that are not.
$test.find('*').not(':visible').remove();
// 3:
var text = $test.text();
// 4:
$test.replaceWith($testclone);
// Now return the text...
return text;
// ...or if you're going to keep going and using the $test variable, make sure
// to replace it so whatever you do with it affects the object now in DOM and
// not the original from which we got the text after removing stuff.
$test = $testclone;
$test.css('background', 'grey'); // For example.
Here is how I did it with MooTools:
$extend(Selectors.Pseudo, {
invisible: function() {
if(this.getStyle('visibility') == 'hidden' || this.getStyle('display') == 'none') {
return this;
}
}
});
Element.implement({
getTextLikeTheBrowserWould = function() {
var temp = this.clone();
temp.getElements(':invisible').destroy();
return temp.get('text').replace(/ |&/g, ' ');
}
})
I search for that and found this question but without solution.
Solution for me is just get out of jquery to use DOM:
var $test = $('#test').get(0).innerText
or if more than on element in array of selector, you need a for loop and a merge but I guess that most of time it is the first version that you need.
var $test = $('#test').get().map(a => a.innerText).join(' ');