Move element before second to last h2 with jQuery - javascript

I am trying to move an elmement with class .books_inarticle_list from its native possition tu just before the second to last H2.
I am using the following which is not working:
$('.books_inarticle_list').insertBefore('#content_column > h2:nth-last-child(2)');
On the other hand something like this works:
$('.books_inarticle_list').insertBefore('#content_column > h2:nth-of-type(6)');
So the issue must be with the nth-last-child() selector, but I don't see what it might be.
Does anyone see anything wrong with that code or knows an alternative way to move that element to just before the second to last H2 tag?

Try the .get() method, which supports negative numbers to go in reverse.
$('.books_inarticle_list').insertBefore($('#content_column > h2').get(-2));
Edit On second thought, it makes more sense to follow your own examples and use the :nth-last-of-type() selector, which will go in reverse.
$('.books_inarticle_list').insertBefore('#content_column > h2:nth-last-of-type(2)');
The reason :nth-last-child() isn't working for you is because that, and :nth-child(), refers to the number of siblings they have in the DOM, not the list of elements returned by the selector.

You'd be better off providing your HTML for the optimal solution, however given your question I'd suggest something like the following:
var $h2s = $("#content_column > h2");
var $secondToLast = $h2s.eq($h2s.length-2);
$('.books_inarticle_list').insertBefore($secondToLast);
I passed on using nth-child just because that will be thrown off any time you modify your HTML structure.

Note, :nth-* selectors use 1-based indexing. You can use :nth-last-of-type() selector with 2 as parameter to selector second from last element of that type, chain .before() with $(".books_article_list") as parameter.
$("#content_column h2:nth-last-of-type(2)").before($(".books_article_list"));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="books_article_list">books</div>
<div id="content_column">
<h2>0</h2>
<h2>1</h2>
<h2>2</h2>
<h2>3</h2>
<h2>4</h2>
<h2>5</h2>
<h2>6</h2>
</div>

i prefer to use ":eq" even ":nth-of-type". So will be :
$('.books_inarticle_list').insertBefore('#content_column > h2:eq('+($('#content_column > h2').length()-2)+')');

Related

How to simplify repeated and convoluted js/jquery code?

This piece of code appears in a js script I have been asked to modify - I'm not sure why it is written in this way, it doesn't make sense to me.
Can anyone help explain what it is doing, and if it can be simplified to be a little more meaningful?
var unformathtml = $(this).text();
if(unformathtml.trim().length>showChar) {
$(this).parent().parent().parent().parent().parent().find('.comment-footer').fadeOut();
}
Lets' pretend we have a DOM like this:
<parent-5>
<target-element>Content</target-element>
<parent-4>
<parent-3>
<parent-2>
<parent-1>
<focused-element>Some Text</focused-element>
</parent-1>
</parent-2>
</parent-3>
</parent-4>
</parent-5>
What this code is saying is "if the text inside of <focused-element> has more characters than showChar then fade out <target-element>.
A better way of doing this would be to give <parent-5> some kind of identifier, which could be an ID or a class, and target that instead of the repeated .parent() call.
Here's an example which showcases the idea:
$('#oldMethod').click(function() {
$(this)
.parent()
.parent()
.parent()
.parent()
.parent()
.find('.comment-footer')
.toggleClass('red');
});
$('#newMethod').click(function() {
$(this)
.closest('.comment-container')
.find('.comment-footer')
.toggleClass('red');
});
.red {
color: #F00;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="comment-container">
<div>
<div>
<div>
<div>
<button id="oldMethod">Old</button>
<button id="newMethod">New</button>
</div>
</div>
</div>
</div>
<div class="comment-footer">Footer</div>
</div>
Wow, that really doesn't make much sense. It is doing this:
1) Getting the raw contents out of an element
2) Checking to see if it is longer than a certain length
3) If so, fading out another element on the page
The parents() thing is very error-prone. It is going up a very precise number of levels in the HTML tree and then descending downwards to find an element with a class of '.comment-footer'. As a result a slight rearrangement of either element in the DOM might result in the code no longer working, because it can't find the specified element.
What you want is to find the tag-to-hide more directly. Ideally, the element-to-hide and the element-that-decides-to-hide would be next to eachother in the DOM (i.e. the element being hidden would be a child or sibling of the element that decides whether or not to hide it). This makes it very easy for the one to find the other. If that isn't possible, your next best bet would be to simply assign an id to the element you are trying to hide and then select on that id directly:
var unformathtml = $(this).text();
if(unformathtml.trim().length>showChar) {
$('#to_hide').fadeOut();
}
As a quick aside, .text() is used (instead of .html()), because the former removes any HTML tags. This way you are measuring the amount of "actual" text inside $(this) to determine whether or not you want to hide said element. So that part is probably fine.

Replacing the inner.html of an unknown named element based on condition

I run into this problem frequently and never know the best approach. Imagine I have a structure that includes several instances of the following html:
<div class="known">
<div class="another unknown">
<div class="unknown">
<h4>Something a something</H4>
</div>
</div>
</div>
For each div of class known, I want to change the inner html of that div only if it contains a div inside it with some particular tag, in this case <h4>.
What is the best way to achieve that. I know that I could do it by taking the inner html of class known and doing a regex match. But is there a more robust way based on tags?
Simple, just use a selector that spans over the div.known and restrict it's context to div h4. If the selector selects at lease one element then the div.class has children as you expect.
if( $('.known div h4').length > 0 ){
$('.known').html('Some html');
}
Yes! You can do this.
var head = Array.prototype.slice.call(document.querySelectorAll("h4 #known"));
for(var i = 0; i < head.length; i++)
{
while(head[i].className !== "known")
head[i] = head[i].parent();
}
Now head will be an array of all the DOM elements have the tag known and have h4's in them.
With jQuery, you can use .has() to narrow your selection and even chain other methods, as in:
$(".known").has("h4").css("background","red");
Check out this fiddle for example. Notice that clicking the button will change the color of any div.known only if that element contains an h4 tag as a descendant.
Documentation on jQuery's .has() -- https://api.jquery.com/has/

CasperJS Click on all links matching a selector

I have read this question and its answer, and wish to take it a little bit further.
I want to use CasperJS.click(selector) function to click multiple links matching a selector. Please note that the links do not have a significant href tag.
Consider the following markup:
HTML:
<div>
<h1 class='myLink'>Cocacola</h1>
<div>
<div>
<h1 class='myLink'>Sprite</h1>
</div>
</div>
</div>
The answers I've mentioned on top here suggest deleting the links so we can click the remaining elements with casper.exists and so on. What if I don't want to manipulate the page?
For reasons beyond my conception, using:
document.querySelector("div .myLink:nth-of-type(1)");
catches the first h1, Cocacola. But:
document.querySelector("div .myLink:nth-of-type(2)");
returns null.
Fiddle here.
Any ideas? Many thanks!
CSS spec for :nth-of-type says that:
The :nth-of-type(an+b) pseudo-class notation represents an element that has an+b-1 siblings with the same expanded element name before it in the document tree, for any zero or positive integer value of n, and has a parent element.
That is, the elements will have to be siblings.
For example,
<div>
<h1 class='myLink'>Cocacola</h1>
<h1 class='myLink'>Miranda</h1>
<div>
<div>
<h1 class='myLink'>Sprite</h1>
</div>
</div>
</div>
The selector div .myLink:nth-of-type(2) will select Miranda. That is, given n siblings of type 'div .myLink', the selector will select the second element from them.
Here is the fiddle for the above example.
Hope this helps!
As mentioned, the reason :nth-of-type(1) works but :nth-of-type(2) doesn't is because there is only exactly one h1 of each type as a child of its parent div. The class selector .myLink is a separate condition entirely and does not affect how :nth-of-type() works.
The reason your first statement appears to return the first element, even though there are technically two elements matching :nth-of-type(1), is because querySelector() returns only the first match.
To obtain the first and second elements matching your selector, use querySelectorAll() instead of querySelector(), and an indexer instead of the :nth-of-type() pseudo-class:
var cocacola = document.querySelectorAll("div .myLink")[0];
var sprite = document.querySelectorAll("div .myLink")[1];

select html object without id

Edit: one missing piece of information - I can't use the class selector because there are more divs with the same class. I already thought of that, but I forgot to mention it. I have no idea why my post got downvoted, but it seems awfully silly considering I provided a lot of information, gave it honest effort, and tried to be verbose with code examples. People on this forum are ridiculous sometimes.
I'm trying to set the id of a div that doesn't have one and there's no way I can give it one upon generation of the page. I've tried using jquery (.each, .contains, .find, .filter, etc.) and I can't seem to get it right. I know a ton of people have asked this question, but none of the answers made sense to me.
I have the ability to set the text (html?) of the div, but nothing else. It ends up looking like this:
<div class="dhxform_note" style="width: 300px;">Remaining letters: 500</div>
I want a handle to the div object so I can show the user how many more letters they can type by updating the text.
Using this:
$("div")
returns a list of all divs on the page. I can see the target div in the list, but I can't get jquery to return a single object.
I know it can also be done with something like this:
var divs = document.getElementsByTagName("div");
for(var i = 0; i < divs.length; i++) {
if( /^Remaining letters/.test(divs[i].innerText) )
divs[i].id = "kudosMsgNote"
}
}
but I was hoping to complete this with a cleaner looking solution involving jquery. I also simply want to know how to do it with jquery, aesthetics not withstanding.
Use a class selector.
var theDivViaTheClass = $(".dhxform_note");
Class Selector (“.class”)
Description: Selects all elements with the given class.
version added: 1.0
jQuery( ".class" )
class: A class to search for. An
element can have multiple classes; only one of them must match.
For class selectors, jQuery uses JavaScript's native
getElementsByClassName() function if the browser supports it.
You seem to be targeting the <div> by its text. Try using the :contains selector:
$("div").filter(':contains("Remaining letters")').first().attr("id", "kudosMsgNote");
The .first() is to make sure you don't set the same id for multiple elements, in case multiple elements contain the text "Remaining letters".
Here's the docs for the :contains selector: http://api.jquery.com/contains-selector/
Be careful, the text you're looking for is case sensitive when using :contains!
Is that div the only one with the class dhxform_note? If so, you can use the class selector:
$('.dhxform_note').html();
With jQuery, you can specify any css selector to get at the div:
$(".dhxform_note").attr("id", "kudosMsgNote");
will get you this element as well.
Selecting on inner text can be a bit dicey, so I might recommend that if you have control over the rendering of that HTML element, you instead render it like this:
<div name="remainingLetters" class="dhxform_note" style="width: 300px">Remaining Letters: 500</div>
And get it like this:
$("[name=remainingLetters]").attr("id", "kudosMsgNote");
However, it's possible that you really need to select this based on the inner text. In that case, you'll need to do the following:
$("div").each(function() {
if ( /^Remaining letters/.test($(this).html()) ) {
$(this).attr("id", "kudosMsgNote");
}
});
If you cannot set id for whatever reason, I will assume you cannot set class either. Maybe you also don't have the exclusive list of classes there could be. If all those assumptions really apply, then you can consider down your path, otherwise please use class selector.
With that said:
$("div").filter(function() {
return /^Remaining letters/.test($(this).text())
}).attr('id', 'id of your choice');
For situations where there are multiple divs with the class dhxform_note and where you do not know the exact location of said div:
$("div.dhxform_note").each(function(){
var text = $(this).text();
if(/^Remaining letters/.test(text)){
$(this).attr("id", "kudosMsgNote");
}
});
EXAMPLE
If, however, you know that the div will always be the 2nd occurrence of dhxform_note then you can do the following:
$("div.dhxform_note").get(1).id = "kudosMsgNote";
EXAMPLE
Or do a contains search:
$("div.dhxform_note:contains('Remaining letters')").first().attr("id", "kudosMsgNote");
EXAMPLE

How to make a function repeat/loop in jQuery?

I have a function here for centering an element within it's parent.
Check out the demo: http://jsfiddle.net/kE9xW/1/
right now it's only applying the centering to the first element, how do i make the function loop itself so it centers every #element on the page. the demo is self explanatory, thanks!
There are a few things you need to do.
As already suggested, id's must be unique, so change the id="..." to class="...". You will also need to change your css to be based on the class not the id (change #element' to '.element')
<div class="container">
<p> ... </p>
<div class="element">
</div>
</div>
Use each in your method to loop over all elements selected by the selector $('.element').
element.each(function(){
// work here in $(this) for the current element
});
You forgot to take the top of the parent div into account, which made all elements overlap each other. So your yPas becomes:
var yPos = $(this).parent().position().top +
parseInt($(this).parent().css('height'))/2 -
parseInt($(this).css('height'))/2 - yPosFromCenter;
Check the working fiddle: http://jsfiddle.net/H99DT/
First, the easiest but most important part: change your IDs to classes. IDs must be unique per page so jQuery's ID selector and JavaScript's document.getElementById() function are only going to give you the first matching element:
Each id value must be used only once within a document. If more than one element has been assigned the same ID, queries that use that ID will only select the first matched element in the DOM.
Change
<div id="container">
...
<div id="element">
to
<div class="container">
...
<div class="element">
and change
$('#element')
to
$('.element')
Next, the more difficult part: you are currently issuing one centerDiv() call to your elements with coordinates from center of 0, 0. That's going to take all your .elements and position them at the exact same spot.
If that's not what you intend, you're going to have to loop through them using .each() and decide the xPosFromCenter and yPosFromCenter in each iteration. It's not clear to me yet how your function works so you may have to explore on your own and see what you can come up with.
Scratch that, see Jamiec's working example for the solution.
Change Id to class in you divs, then make container's position relative with css, and I'll suggest make jQuery plugin from your function. See results http://jsfiddle.net/kE9xW/1/

Categories

Resources