Forgive my ignorance, I'll try to be as clear as possible. I'm trying to modify an existing JS function that adds a class to a parent element. I would like to modify it to add the same class to a sibling of the parent, in this case the aside '.footnote-right-col'.
I have tried a good number of ways but this is beyond me. I'm not sure if I need to create a new function and call it separately, or if I can simply add a new variable to this function. I presume the way I would target the parent is via getParent.nextSibling
As is JS
open: function (el) {
this.footnote.el = getParent(el, '.footnote-container')
var popover = this.footnote.popover()
this.footnote.el.classList.add('is-open')
this.sizeFootnote()
popover.classList.remove('is-hidden')
this.positionFootnote()
popover.classList.add('is-visible')
window.addEventListener("resize", self.resize.bind(self))
},
Rendered HTML
<div class="footnote-container open-down">
<button class="footnote-button" title="view footnote #1">...</button>
<aside class="footnote-popover is-hidden">...</aside>
</div>
<aside class="footnote-right-col is-hidden">...</aside>
this.footnote.el.nextElementSibling Should get you .footnote-right-col element if I am understanding how you set things. If this.footnote.el is the <div class="footnote-container open-down"> element then my answer works. Once you have that div you can just call the .nextElementSibling
EDIT:
ok so cool if this.footnote.el is the button then simply do this:
this.footnote.el.parentElement.nextElementSibling and you should have it!
So to add the class:
this.footnote.el.parentElement.nextElementSibling.classList.add('is-open')
Related
I need a little bit of help with my shopify store. I'm trying to change the class of a button in order to style it from the default class "btn" to "askAQButton". However, the code for the buttons are not in the markup at all and so I can only assume they're generated by JS by the shopify plugin itself.
I figured it should be simple and I should be able to just target the element by "id" with jquery and change class. However, the button doesn't have an ID and neither does the href...
My next thought was okay, target the parent div by id, then target the class of the button and href and change it that way (to avoid targeting duplicate classes).
I just can't seem to get it working.
Here is all the markup I'm given to implement this onto my store page:
Obviously a lot of it is irrelevant (probably all of it besides the parent div) however, when I load my page up after implementing this code it auto-generates this right under the second input:
<div class="wk-ask-order">
<button class="wk-ask-a-question-order btn" style="display: inline-block;">Raise a Query
</button>
View Your Queries
</div>
Can anyone help me with targeting it and changing the class names please.
I'm not sure if I get the question right, but there might be a problem with the button having not yet been injected into the DOM at the time when your script is targeting it.
Try waiting for the DOMContentLoaded event before executing your code:
document.addEventListener('DOMContentLoaded', () => {
const askAQBtn = document.querySelectorAll('.wk-ask-a-question-order')[0]; //there's probably a better selector
askAQBtn.classList.replace('btn', 'askAQButton');
});
If that doesn't work, have a look at MutationObserver to wait for the specific element to exist. I found a snippet for this on Github:
https://gist.github.com/jwilson8767/db379026efcbd932f64382db4b02853e
This was my answer it's a bit lo-fi but I hope it gives you something to work from. I followed your idea regarding the parent div id.
let parent = document.getElementById('wk-askme');
let child_nodes = parent.childNodes;
for (let i = 0; i < child_nodes.length; i++) {
let item = child_nodes[i];
if ($(item).attr('class') == "wk-ask-a-question-order btn") {
$(item).attr('class', 'wk-ask-a-question-order askAQButton');
}
}
On a web page we have a list of profiles. On the right hand side of the profile is some text, followed by an arrow img#arrow.
When img#arrow is clicked, we have the following jQuery we hope to run:
However, the corresponding .bottom-sec is not toggling.
jQuery('#arrow').click(function(){
var $parent = $(this).parent();
$($parent).addClass('active');
jQuery($parent +' .bottom-sec').toggle();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="profile-right">
<h2>Bob Brown</h2>
<h3>Non-Executive Chairman</h3>
<p>Intially showing text.</p>
<div class="bottom-sec" style="display: none;">
<p>Initially hidden text.</p>
</div>
<img id="arrow" src="/wp-content/themes/wtc/images/icons/down-arrow-circle-hi.png">
</div>
Problem
The problem with your code is exactly what the comment on your question is saying, but he didn't explain anything:
You're combining two different ways of selecting elements. One is with selectors, the other is traversing. You're using them in a way which isn't possible (the $parent + ' .bottom-sec' part). The comment linked to a jQuery page about traversing which you should definitely read! It tells you a lot about how to use traversing functions, which you could use!
Solution
There are multiple solutions to this, but I'll write down the one I think is the best:
First of all, change the HTML a bit. I've removed the element style of .bottom-sec and changed the id of the image to a class, because you have multiple images with the same id on the page, which is not a recommended thing to do. Classes can occur more than once, id's cannot.
<div class="profile-right">
<h2>Bob Brown</h2>
<h3>Non-Executive Chairman</h3>
<p>Intially showing text.</p>
<div class="bottom-sec">
<p>Initially hidden text.</p>
</div>
<img class="arrow" src="/wp-content/themes/wtc/images/icons/down-arrow-circle-hi.png">
</div>
I've reduced the JavaScript to the following. Note that is just reduced to one line, where a click on the .arrow element goes searching for the closest .profile-right parent. If, for whatever reason, you decide to change the HTML and the .arrow element is no longer a child of the .profile-right, this code still works. The only thing it does is toggle an active class on the .profile-right.
jQuery(document).on('ready', function() {
jQuery('.arrow').on('click', function(){
jQuery(this).closest('.profile-right').toggleClass('active');
});
});
The document ready listener was added because of OP's comment.
With CSS, we can use the new .active class to show or hide the element.
.profile-right .bottom-sec {
display: none
}
.profile-right.active .bottom-sec {
display: block
}
Original Code Fix
If for some reason you wanted to use your original code, this is how it should be:
// Nothing wrong about this part.
// Your only worry should be that there could be
// multiple elements with the same ID, which is something really bad.
jQuery('#arrow').click(function(){
// This part is correct, no worries
var $parent = $(this).parent();
// Removed the $(...), because $parent is already a jQuery object
$parent.addClass('active');
// Changed the selector to a find function
$parent.find('.bottom-sec').toggle();
});
You could also combine all of the code inside the listener function to just one line:
jQuery('#arrow').click(function(){
$(this).parent().addClass('active').find('.bottom-sec').toggle();
});
Change your js code like below.
jQuery('#arrow').click(function(){
var $parent = $(this).parent();
$($parent).addClass('active');
jQuery($parent).find('.bottom-sec').toggle();
});
In your event listener you can catch the element (the down arrow) that triggered the event. It will be referred as this.
Then you can go through the DOM tree using .next() and .parent() to access the <div> to toggle.
Note: you may need more functions than the one I explained above.
Note 2: without code or more detailed information, we can't help you further, I will edit this answer if you add details.
I have a page where a plugin adds child div's to a parent div at random time interval.
The basic structure is :
<div id="parent">
<div class="child" someattr="abc">content1</div>
<div class="child" someattr="xyz">content2</div>
</div>
I tried the following to detect adding of elements and it works
var c = document.getElementById('parent');
c.__appendChild = c.appendChild;
c.appendChild = function(){
alert("Added");
c.__appendChild.apply(c, arguments);
}
I got the above from an answer in stackoverflow itself.
What I would also like to do is to get the contents of the child divs.
That is, their attributes and the content inside.
What would be the easiest/best way to go on this.
I wouldn't mind if the solution is jquery
No jQuery needed:
document.getElementById('parent').innerHTML;
Of course you could use jQuery as well, because it might help you get the individual children:
$('#parent .child').each(
function(){
// In this context, 'this' points to the current child element in the itertion
}
I'm quite new to javascript and JQuery programming. Usually, to access elements I give them an id, so I can get them like $("#"+id).blabla().
But now I need to dynamically create a div, and access elements inside it.
Something like
<div id="automaticallyGeneratedId">
<div ???></div> <!-- first div -->
<div ???></div> <!-- second div -->
</div>
What are the best practices to access and identify each of the inner divs?
I generate another id for them?
Or what?
I don't have the theory of selectors fully clear.
edit: modified the question from identifying a single inner div to identifying divs amongs many of them
You can maintain a pattern when you're generating id. For example:
if you always generate id like: myid1, myid2,myid3...
<div id="myid1">
<div></div>
</div>
<div id="myid2">
<div></div>
</div>
......
then you can try:
$('div[id^=myid]').find('div').foo();
OR
$('div[id^=myid] div').foo();
Here, ^= is start with selector, so div[id^=myid] will select div whose id start with myid.
You can also use Contain word selector which is ~= and use like $('div[id~=myid]'). This will select div with id contains word myid.
Instead of id if you want to use other attribute eg. name then change selector like:
$('div[name^=myid]') or $('div[name~=myid]').
It's usually a good practice that if you already have a reference to that outer div to just search from there using find.
You can give it an id, or if you want to use a more general approach you can use classes.
<div class="subdiv">...
$('#automaticallyGeneratedId').find('div.subdiv')
Usually, when you create them, you can assign event handlers and the likes straight on them. Like this:
var div = $( '<div></div>' );
div.on( 'click', function() {
// Do something when the generated div is clicked
});
// Then, add it to the DOM
$( 'body' ).append( div );
You don't need to bother selecting them with ID or classes, they're already available in your code.
Another way is to use event bubbling to handle newly created elements of the same class. A good link about this is this one: http://beneverard.co.uk/blog/understanding-event-delegation/
Many ways you can create an element and give him an Id or Class, or use the DOM to access it..
$("html").prepend('<div id="foo"></div>');
$("#foo").doSomething();
another way
$("#automaticallyGeneratedId").find("div").doSomething();
To access the div in the element with the id:
$("#automaticallyGeneratedId div").whatever
If you cache the divs you could use something like:
var myDiv1Child = $('div', myDiv1);
Create a delegated listener and within the listener you can find the element by doing this
//If a div inside the parent is clicked then execute the function within
$('.PARENT_CLASS').click("div", function(){
//This variable holds all the elements within the div
var rows = document.querySelector('.PARENT_CLASS').getElementsByTagName('div');
for (i = 0; i < rows.length; i++) {
rows[i].onclick = function() {
console.log(this); //The element you wish to manipulate
}
}
});
Update: Everyone that contributed, it's well appreciated, you all are very kind and generous and all of you deserve my dear respect. Cheers.
Note: I'm making a simple jQuery tooltip plugin, the tooltip will fire on mouseover. The mouseover will create an instance of the div tool-tip that will be specific to each anchor that launched the div tool-tip. So each anchor with the class .c_tool will have its own created div that will erase after mouseout. Anyway all those details are irrelevant. What is important is how to create a div with .append() or .add() on and then find a way to call it and apply actions to that div without setting an identifier (id), class, or any means to identify it.
I know theres a way you could find the div by counting, so if you gave every created div the same class and then counted them to find that one, however I don't know if this is the most efficient method that is why I'm asking for help.
I'm not going to post the whole plugin script thats unnecessary, so I'll paste a simplified version.
hover me
hover me
$(document).ready(function() {
obj = $('a.c_tool');
obj.mouseover(function() {
/// append div to body it will be specific to each item with class c_tool, however I don't want to set an ID, or CLASS to the appended div
}).mouseout(function() {
/// remove added div without setting ID or class to it.
});
});
Working example:
http://jsfiddle.net/xzL6F/
$(document).ready(function() {
var tooltip;
obj = $('a.c_tool');
obj.mouseover(function() {
var element = $('<div>', {
html: "I'm a tooltip"
});
tooltip = element.appendTo($("body"));
/// append div to body it will be specific to each item with class c_tool, however I don't want to set an ID, or CLASS to the appended div
}).mouseout(function() {
tooltip.remove();
/// remove added div without setting ID or class to it.
});
});
To create a new DOM node you can use the jQuery constructor, like
$(document).ready(function() {
obj = $('a.c_tool');
obj.mouseover(function() {
if(!$.data(this, 'ref')) {
$.data(this, 'ref', $ref = $('<div>', {
html: 'Hello World!'
}).appendTo(document.body));
}
}).mouseout(function() {
$.data(this, 'ref').remove();
});
});
.appendTo() returns the DOM node of invocation (in this case, the newly created DIV) as jQuery object. That way you can store the reference in a variable for instance and access it later.
Referring your comment:
To remove all stored references, you should do this:
$('a.c_tool').each(function(index, node) {
$.removeData(node, 'ref');
});
you can use $.append(
);
http://api.jquery.com/append/
and to find the DOM created dynamically u can use
$("#dynamicallyCreatedDOMid").live("yourCustomTrigger",function(){
});
http://api.jquery.com/live/