Jquery add class if a element exists within div - javascript

I want Jquery to add a class .pointer to .staff-container if <a href=""> exists within .staff-picture.
My Jquery:
if($('.staff-container').find('.staff-picture').closest('a').length) {
$(this).addClass('pointer');
}
No class is being added with the above jquery, what am I doing wrong?
My Css:
.pointer {
cursor:pointer !important;
}
My HTML:
<div class="teachers">
<div class="span12">
<div class="scroll transparent">
<div class="staff-outer-container">
<div class="staff-container">
<div class="staff">
<div class="staff-picture">
<img src="img/people/teachers/ahmed.png" />
</div>
<p><span class="bold">Mr. Ahmed</span><br />
Ext. 13417<br />
Room 417/323<br />
Ahmed#wcskids.net</p>
</div>
</div>
<div class="staff-container">
<div class="staff">
<div class="staff-picture">
<img src="img/people/teachers/aiello.png" />
</div>
<p><span class="bold">Mr. Aiello</span><br />
Ext. 13328<br />
Room 328/323<br />
ASusan#wcskids.net</p>
</div>
</div>
<div class="staff-container">
<div class="staff">
<div class="staff-picture">
<img src="img/people/teachers/aiosa.png" />
</div>
<p><span class="bold">Mr. Aiosa</span><br />
Ext. 13419<br />
Room 419/323<br />
BAiosa#wcskids.net</p>
</div>
</div>
</div>
</div>
</div>
</div>

You can do this :
$('.staff-picture a').closest('.staff-container').addClass('pointer');
I hope the logic is obvious from the code.

I would first iterate through the .staff-container divs, then use a conditional to determine which ones have an a tag using the this object as context:
//Goes through all the .staff-picture divs
$('.staff-container').each(function(){
//If the a tag exists within the current .staff-picture (returned object isn't undefined)
if($('a', this).html() != undefined){
//Add the class if a exists
$(this).addClass('pointer');
}
});
http://jsfiddle.net/y9ZKG/
EDIT
Sorry, I meant staff-container, not staff-picture. But, either will work.
2nd EDIT
Also, if you are curious why your original methodology wasn't working, it is because the conditional you use (the first if) does not instantiate the this object. That is, this does not exist inside your function.

Try this:
var $selector = $('.staff-container');
if($selector.find('.staff-picture').has('a')) {
$selector.addClass('pointer');
}

$.closest() traverses up not down the tree, therefore you are not finding anything. see the jQuery API

I like to approach these types of problems with "reverse" logic:
$('.staff-picture a').parent().parent().parent().addClass('pointer);
There's probably a way to "choose" your staff-container parent, but if the DOM structure doesn't change, this works great.
This starts by selecting all the a-links and then ONLY applies those up which uses jQuery's powerful selection code. It doesn't rely on tests with if-statements.

Related

Identifying an HTML element that has *no* attributes of any kind with JavaScript?

I have a tool that is used for cleaning up crappy HTML in order to make sense of the underlying structure. Having stripped class, style attributes and various Angular attributes, often the resulting markup is a series of nested <div> or <span> elements that have no attributes. What I would like to do is provide option to do a second pass where a <div> or <span> with no attributes can be removed, to flatten the structure more.
Is there a way in JavaScript to confirm that an HTML element has no attributes of any kind?
And if that is possible, how might I approach this stripping of an element?
For example, assuming I have this:
<div>
<div>
<div id="blah">
<div>
<div>
<span dir="auto">
<span>Joe Bloggs</span>
</span>
</div>
</div>
</div>
</div>
</div>
That should end up as:
<div id="blah">
<span dir="auto">
Joe Bloggs
</span>
</div>
Which I would then format to:
<div id="blah">
<span dir="auto">
Joe Bloggs
</span>
</div>
So I'd need a function that can walk the DOM and remove a div (or span) that has no attributes while leaving the inner contents intact (unless of course any of those inner elements can also be stripped for same reason).
Any pointers before I go ahead and construct a shoddy (but working) script would be appreciated!
The attributes property will tell you how many attributes an element has.
const countAttributes = element => console.log({
count: element.attributes.length,
list: [...element.attributes].map(attribute => attribute.name)
});
const divs = document.querySelectorAll('div');
divs.forEach(countAttributes);
<div></div>
<div class="one attribute"></div>
<div class="two attributes" id="second attribute"></div>
Do note that an element without attributes might still be used for something (e.g. a stylesheet might reference it in relation to other elements).
Here's how I did it.
I created a demo element, to get the elements, then I checked the number of elements, I checked if the element should be stripped.
I replaced the element with its children, and if it didn't have any, I used its text
function strip(startElement, toStrip) {
const test = document.createElement('div');
test.innerHTML = startElement.outerHTML;
[...test.querySelectorAll('*')].forEach(elem => {
if (!elem.attributes.length && toStrip.includes(elem.tagName.toLowerCase())) {
if (elem.children.length) elem.replaceWith(...elem.children);
else elem.replaceWith(elem.innerText);
} ;
});
return test.innerHTML;
}
console.log(strip(document.querySelector('div'), ['span', 'div']));
<div>
<div>
<div id="blah">
<div>
<div>
<span dir="auto">
<span>Joe Bloggs</span>
</span>
</div>
</div>
</div>
</div>
</div>
Updated Code
Here you go.
document.querySelectorAll("div").forEach((ele) => {
if (ele.attributes.length === 0) {
var fragment = document.createDocumentFragment();
while (ele.firstChild) {
fragment.appendChild(ele.firstChild);
}
ele.parentNode.replaceChild(fragment, ele);
}
});
<div>
<div>
<div id="blah">
<div>
<div>
<span dir="auto">
<span>Joe Bloggs</span>
</span>
</div>
</div>
</div>
</div>
</div>
So final output would be
<div id="blah">
<span dir="auto">
<span>Joe Bloggs</span>
</span>
</div>

Problem trying to select next item in JQuery

I'm trying to select the next element to add the class image-radio-selected with JQuery.
My html is like
<div id="photocall">
#foreach ($photocalls as $photocall)
<div class="d-inline-block mx-1">
<div style="background-image: url('')" class="photocallThumb image-radio-selected"></div>
</div>
#endforeach
<input>
</div>
Im trying to:
$( "#mybutton" ).on("click", function() {
var selected = $('.photocallThumb.image-radio-selected'); // working
selected.next('.photocallThumb').addClass('image-radio-selected'); // not working
});
After 2 hours, trying to solve, reading doc,
I'm more confused than when I started...
what am I doing wrong?
One method is you will need to get out of the parent div, then do a next for the parent.
$( "#mybutton" ).on("click", function() {
var selected = $('.photocallThumb.image-radio-selected');
selected.parent(".d-inline-block").next(".d-inline-block").find('.photocallThumb').addClass('image-radio-selected'); // not working
});
.image-radio-selected{border:1px solid #ff00aa;}
.mx-1{width:100px;height:100px;border:1px solid #000000;}
.d-inline-block{display:inline-block;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="photocall">
<div class="d-inline-block mx-1">
<div style="background-image: url('')" class="photocallThumb image-radio-selected"></div>
</div>
<div class="d-inline-block mx-1">
<div style="background-image: url('')" class="photocallThumb"></div>
</div>
<div class="d-inline-block mx-1">
<div style="background-image: url('')" class="photocallThumb"></div>
</div>
<div class="d-inline-block mx-1">
<div style="background-image: url('')" class="photocallThumb"></div>
</div>
<button type="button" id="mybutton">next</button>
</div>
JQuery's next method selects the next sibling of the selected element. However, since your photocallThumb div is inside a d-inline-block div, it has no siblings. You'd have to go back up a level, then find the next photocallThumb, maybe something like selected.parent().find('.photocallThumb').eq(0).
However, an even better pattern that will help you avoid bugs like these is called templating. Basically, on the client side, you have an html template string, and you pass it data that represent your current state. In your case, you'd pass it an array of javascript objects, each one with an image url and an isSelected boolean. Then, when your state changes, instead of using jquery to try to fix what's changed, you just rerender your template and replace your html element's content with the newly rendered template, and it's now magically in the correct state. This is the pattern favored by large frameworks like React and Angular.
Here's an example from lodash that renders a list of usernames:
// Use the "evaluate" delimiter to execute JavaScript and generate HTML.
var compiled = _.template(
`<% _.forEach(users, function(user) { %>
<li><%- user %></li>
<% }); %>`);
compiled({ 'users': ['fred', 'barney'] });
// => '<li>fred</li><li>barney</li>'

Remove children with defined attribute - not working

Trying to remove children DIV elements of a parent with certain attribute. I have it half working, but with the below code, it doesn't find the children
HTML
<div id="PremiumGiftContainer" class="PremiumGiftContainer">
<div class='message' is-vip='false'>
<p>FALSE</p>
</div>
<div class='message' is-vip='false'>
<p>FALSE</p>
</div>
<div class='message' is-vip='true'>
<p>TRUE</p>
</div>
</div>
<button id="button">Remove</button>
JQUERY
$("button").on("click", function(){
remove_element();
})
function remove_element(){
$('#PremiumGiftContainer').children(function () {
$("[is-vip]").each(function(){
if($(this).attr('is-vip')=='true'){
$(this).fadeOut();
}
});
})
}
FIDDLE
If I remove the $('#PremiumGiftContainer').children... section, it works, but I was trying to limit the scope of the search that needs to happen to find the correct switches.
Is what I'm trying to do achievable?
children() does not accept a function, it takes a selector. As such you can simply use an attribute selector and then call fadeOut() on the resulting elements.
Also note that you should not create your own non-standard attributes on elements. If you want to store custom data with an element, use a data-* attribute.
$("button").on("click", function() {
remove_element();
})
function remove_element() {
$('#PremiumGiftContainer').children('[data-is-vip="true"]').fadeOut();
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="PremiumGiftContainer" class="PremiumGiftContainer">
<div class="message" data-is-vip="false">
<p>FALSE</p>
</div>
<div class="message" data-is-vip="false">
<p>FALSE</p>
</div>
<div class="message" data-is-vip="true">
<p>TRUE</p>
</div>
</div>
<button id="button">Remove</button>
Can do this with one selector using an attribute selector
$('#PremiumGiftContainer > [is-vip=true]').fadeOut()
DEMO

Hide only on certain elements

I'm using a script that checks for any tag that also has a SRC="self". My function should function like this:
Check if img src="self"
If true, hide the parent div
If false, do nothing
Currently the function actually hides every img regardless of src. If I replace the jQuery hide() action then the function works perfectly. It just seems like it isn't quite performing the hide function like I anticipated.
function changeSourceAll() {
var images = document.getElementsByTagName('img');
for (var i = 0; i < images.length; i++) {
if (images[i].src.indexOf('self') !== -1) {
$(".redditThumbnail").hide();
}
else (){}
}
}
changeSourceAll();
Sample HTML is below. I have multiple .listrow div elements identical to this and the function removes all the .redditThumbnail divs.
<div class="listrow news">
<div class="newscontainer">
<div class="redditThumbnail"></div>
<div class="articleheader news">
<div class="actionmenu">
<p class="mediumtext floatleft alignleft">
author
</p>
<div id="redditUsername"></div>
<div class="floatright">
<div class="redditPermalink material-icons"></div>
</div>
</div>
<p class="redditTitle mediatitle news"></p>
</div>
</div>
</div>
Thanks!
You could use an attribute-equals selector to find all of the <img> elements that point to "self" and then hide their parents :
// Hide the closest thumbnail for elements that match this constraint
$('img[src="self"]').closest('.redditThumbnail');
Example
$(function() {
$('button').click(function(){
$('img[src="self"]').closest('.redditThumbnail').hide();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='redditThumbnail'>
A (has self)
<img src='self' />
</div>
<div class='redditThumbnail'>
B (doesn't have self)
<img src='self-test' />
</div>
<div class='redditThumbnail'>
C (has self)
<img src='self' />
</div>
<hr />
<button>Hide Self-Referencing Images</button>
The issue with hiding every img is because you select and hide all .redditThumbnail elements for every matching item. To fix this you could use this:
$(images[i]).closest('.redditThumbnail').hide();
However a better approach entirely would be to use filter() and find only the .redditThumbnail elements which match the requirements. Try this:
$('.redditThumbnail').filter(function() {
return $(this).find('img[src="self"]').length != 0;
}).hide();
You are hiding all: $(".redditThumbnail").hide();. I guess you should do something like $(images[i]).hide();

Select div children

I want to select the child of a div using jquery. I try with children() but didn't work
<div class="main" id="this_456" onclick="change(456)">
<div id="title">some text</div>
<div id="body">some text as well</div>
</div>
javascript
function change(id)
{
$('#this_'+id).children("#body").fadeOut();
}
Your code works if you specify 456 as the argument rather than this_456 (see: http://jsfiddle.net/aLxTz/).
However, since <div id="body"/> is identified by ID (#body) it's redundant to look for it inside and other element - it should be unique document-wide. Use the class="" attribute if you expect to have several instances of a body <div/>, e.g. <div class="body">...</div>.
Furthermore, note that the onclick handler has the this variable set to the context element. Since this is the element in question itself, you could write
<div class="main" id="this_456"> ... </div>
$(".main").click(function() {
$(this).chlidren(".body").fadeOut();
});
<div class="main" id="this_456" onclick="change(this)">
<div id="title">some text</div>
<div id="body">some text as well</div>
</div>
function change(elm) {
$(elm).children("#body").fadeOut();
}
FIDDLE
Try this code:
function change(id) {
$('#this_'+id+ ' > div').find("#body").fadeOut();
}

Categories