I have a question about dom navigation with jquery. I'm trying to find an element with a given class that is closest in the dom following a given element.
I have a table like structure, created through divs and styled in css. I have an element being edited, and when the user presses enter I want to focus the following editable element. However, it's not a sibling of the element being edited.
HTML
<div class="calendarEntry">
<div when="2014,9,18" class="when">Sep 18</div>
<div class="items">
<div class="item">
<div code="ABC" class="type">ABC123</div>
<div offered="2014,9,15" class="offered dateish">Sep 15
<div class="offer editable">10</div>
<div class="sku editable">TH1</div>
<button>Publish</button>
</div>
</div>
<div class="item">
<div code="DEF" class="type">DEF321</div>
<div offered="2014,9,14" class="offered dateish">Sep 14
<div class="offer editable">10</div>
<div class="sku editable">TH2</div>
<button>Publish</button>
</div>
</div>
<div class="item">
<div code="GHI" class="type">GHI852</div>
<div offered="2014,9,12" class="offered dateish">Sep 12
<div class="offer editable">10</div>
<div class="sku editable">TH3</div>
<button>Publish</button>
</div>
</div>
</div>
</div>
Note: There are multiple calendar entries on the page.
Say the user is editing the offer of the DEF312 item. When they hit enter I want to edit the offer of GHI852. I have the code to make the div editable, by replacing it with a text field with a class of offer editing. If they're editing the final offer in this calendar entry, then the enter key should focus the first editable offer of the following calendar entry, if there is one. If we're at the bottom of the list I don't want to wrap back to the top (which I think would overly complicate matters anyway).
The bit I'm stuck with is how to find the next offer (all offers are editable).
Here's what I've tried:
var nextOffer = $('.offer').find('.editing').next('.editable');
Clearly, this doesn't work. The problem is that the following editable offer isn't a sibling of the current offer being edited, so next() doesn't work for me. The following offer could be in the current calendar entry, or it's just as likely to be in the next calendar entry. Either way, it's a few divs away, at varying depths.
Can I even do this with jquery dom traversals, or am I better just brute forcing it through javascript (i.e. looping through all .editable instances and returning the one after .editing?
Adding the class 'editing' to simulate the the input:
<div class="item">
<div code="DEF" class="type">DEF321</div>
<div offered="2014,9,14" class="offered dateish">Sep 14
<div class="offer editable">10</div>
<div class="sku editable editing">TH2</div>
<button>Publish</button>
</div>
</div>
you can do:
function findEditable(currentItem) {
var nextEditable = undefined,
selectors = [".item", ".calendarEntry"];
$.each(selectors , function (idx, selector) {
var ref = currentItem.closest(selector);
nextEditable = ref.parent()
.children("div:gt(" + ref.index() + ")")
.find(".offer.editable")
.first();
return nextEditable.length === 0;
})
return nextEditable;
}
findEditable($(".editing")).css({
color: 'red'
});
jsfiddle demo
You can use parents() to get the .offered element which contains the .offer element like so:
var offered = $('.offer').find('.editing').parents('.offered');
From that you can use next() to get into the .offered element's sibling .item element, and find the .editable element within that:
offered.next('.item').find('.editable');
JSFiddle demo. Note that I've manually added this .editing element within your DEF321 item's .offer element - I assume this gets added dynamically on your side, but either way isn't included in your question.
Edit: The HTML in the question has now been changed. Based on this, instead of getting the .offered parent, you'd get the .item parent:
var item = $('.offer').find('.editing').parents('.item');
And proceed in the same way as before:
item.next('.item').find('.editable');
JSFiddle demo.
try this
var current=document.activeElement,
all=$(".editable"),
index=all.indexOf(current),
next=all[index+1]
It first finds the current element and the list of elements,
then it will find the current element in the list.
It will then add 1 to the index and select it from the list.
To extend the array with the indexOf function;
if(!Array.prototype.indexOf){
Array.prototype.indexOf=function(e/*,from*/){
var len=this.length>>>0,
from=Number(arguments[1])||0;
from=(from<0)?Math.ceil(from):Math.floor(from);
if(from<0)from+=l;
for(;from<len;from++){
if(from in this&&this[from]===e)return from;
}
return -1;
};
}
Related
site i am selecting from looks roughly like this
<div class="start-left">
...
<div class="news-post">...</div>
<div class="news-post">...</div>
<!-- want to get this one above -->
<div class="news-post">...</div>
<div class="news-post">...</div>
<div class="news-post">...</div>
<div class="news-post">...</div>
...
</div>
tried this but didnt work on either firefox or chrome
document.querySelector('.start-left div:nth-child(2)')
is this even possible or do i need to rething how i am doing this? I am using puppeteer for a webscraper and need to be able to press a link in a specific news post, e.g the second one
nth-child(n) counts all children of the element, regardless of the type of element (tag name). If there are other elements of different type coming before your target element nth-child will fail to find the correct element and may return null.
However, the selector nth-of-type(n)
matches elements based on their position among siblings of the same
type (tag name)
and ignores elements of a different type.
// nth-child(2) returns null because the 2nd element is not a div
var wrongElement = document.querySelector('.start-left div:nth-child(2)');
// nth-of-type(2) filters using the type of element
var correctElement = document.querySelector('.start-left div:nth-of-type(2)');
console.log('div:nth-child(2): ' + wrongElement);
console.log('div:nth-of-type(2): ' + correctElement.outerHTML);
<div class="start-left">
<p class="news-post">...</p>
<p class="news-post">Not this</p>
<div class="news-post">...</div>
<div class="news-post">This one</div>
<!-- want to get this one above -->
<div class="news-post">...</div>
</div>
You could use your work-around by adding the number of preceding elements to the selector, eg nth-child(4), however, a more robust solution is to use nth-of-type(2).
I have 3 forms section on a web page. Each form section has a seperate id with input elements contained within. I have a div outside of those form elements that serves as header for each section. I've written a script that checks if the form section has an input element that are labelled by the class name '.form-input'. Problem is when no form-inputs are found My script hides all Titles with the ".title-section" class name. I need to hide only the "title-section" that belongs to the form element. It would be easier if this title were contained within the form but it's outside as a seperate div.
$(function () {
//If there is no form inputs hide title section
// .lenght is truthy = true or false
if (!$(".form-input").length) {
$(".title-section").hide();
}
});
See below html for structure
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="title-section">
<h3>Hide Me 1</h3>
</div>
<div id="form-section">
<div class="well">
<input class="form-input">
</div>
</div>
</div>
</div>
</div>
It is hiding all .title-section elements simply because $('.title-section') selects all elements matching that selector, regardless of whether there is a .form-input element that follows it. You therefore need some code that assesses whether or not there is a .form-input element following the .title-section element. Something like this would work:
var titlesToHide = $('.title-section').filter(function(){
return $(this).next().find('.form-input').length === 0;
});
titlesToHide.hide();
I'm using jQuery's .filter() method to select the desired elements. In the code I wrote, it looks at each .title-section element, and then checks to see if the element right after it—.next()—has an element inside it with the class name .form-input. If it doesn't—.length === 0—the filter function returns true, thus including that .title-section element in the final collection.
JS Fiddle: http://jsfiddle.net/hca1y15z/
I am building a jQuery plugin to manage form collections. The plugin aims to add add, remove, move up and move down buttons to alter that collection.
A collection's root node always contains a selector, such as .collection.
A button can be anything as soon as it has the .add class
I implemented min and max options, so add and remove buttons disappear accordingly. My problem comes up when I try to manage a collection of form collections: how to select only the add buttons that refers to the right collection?
To simplify the problem, look at the following HTML code:
<div class="collection">
<div>something</div>
<div>something</div>
<div>
<div class="add">+</div>
</div>
<div>something</div>
<div class="collection">
<div>something</div>
<div>something</div>
<div>
<div class="add">+</div>
</div>
<div>something</div>
</div>
</div>
Keep in mind that the button can be arbitrary deep: collection is built by an user and I don't know where can be the button in the dom. BTW, it is deeper than the .collection, that's all I know.
How to select all add buttons until the second .collection, but not further?
For those interested, this plugin is available (but in active dev) here.
I will assume you have a reference to the .collection object that you want to find the add buttons for in a variable called target. If so, you can do it like this:
target.find(".add").filter(function(i, element) {
return $(element).closest(".collection").get(0) === target.get(0);
});
This finds all the .add buttons that are in a given .collection and then removes any who are contained in a nested .collection instead of directly in the target .collection.
Try
$(".add").not($(".collection:gt(0) .add"));
Note,
Utilizing jQuery .not()'s .not( selector ) , where selector is selctor string
.not( selector ) version added: 1.0
selector Type: Selector or Element or Array A string containing a
selector expression, a DOM element, or an array of elements to match
against the set.
$(".add").not(".collection:gt(0) .add") http://jsfiddle.net/47wc5L96/21/
did not appear to return same results as .not( selection ) , where selection is jQuery object
.not( selection ) version added: 1.4
selection Type: jQuery An
existing jQuery object to match the current set of elements against.
$(".add").not($(".collection:gt(0) .add")); http://jsfiddle.net/47wc5L96/20/
console.log($(".add").not($(".collection:gt(0) .add")));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<div class="collection">
<div>something</div>
<div>something</div>
<div>
<div class="add">+</div>
</div>
<div>something</div>
<div class="collection">
<div>something</div>
<div>something</div>
<div>
<div class="add">+</div>
</div>
<div>something</div>
</div>
</div>
I have the next code dynamically created using JQuery. Theere are multiple row class divs placed one under the other.
<div class="row">
....
</div>
<div class="row">
<div class="line_type"></div>
<div class="download_value"></div>
<div class="flag"></div>
<div class="email"></div>
<div class="prize"></div>
</div>
<div class="row">
....
</div>
After i create these divs I have a "pointer" to a specific div which is of class row. In JQuery, how do i make it so I go down the DOM tree, until i reach the div of class line_type and download_value and remove them both, and also I'd like to go one more node down, at the div of type email and change some of it's CSS attributes.
I was not able to find anything on the web, maybe it's cause i'm a noob at these still.
I have a "pointer" to a specific div which is of class row ->
Assuming that you have the this object of the corresponding div with class row.. then you can use .find to get the line_type and download_value inside that div.
$(this).find('.line_type').remove();
$(this).find('.download_value').remove();
Then you can use the same .find to get the div with class email and access the .css
$(this).find('.email').css(/* You code*/);
Assuming row_pointer points to the row in question:
$('.line_type, .download_value', row_pointer).remove();
$('.email', row_pointer).css(...);
check this out
$('div.row').bind('click', function() {
$this = $(this);
$('div.line_type, div.download_value', $this).remove();
$('div.email', $this).css('background-color', 'red');
});
http://jsfiddle.net/YvyE3/
I'm trying to select the text from the name attribute of the anchor element of the last Comment div in the Comments div (i.e. comment_3037) How can I select it?
Here's my html:
<div id="Comments">
<div class="Comment"><!-- last "Comment" element in the div -->
<a name="comment_3037"></a>
<img src="">
<div>
<div class="Stats">Some info goes here</div>
<div class="Body">Comment goes here.</div>
</div>
</div>
</div>
Corrected Version
(dojo.query always returns a nodelist)
This would look something like that:
var nodelist = dojo.query('#Comments > .Comment:last-child > a[name]);'
var value = dojo.attr(nodelist.at(0), 'name');
Explanation: #Comments > .Comment selects all nodes with class Comment inside the node with id Comments. :lastChild reduces this selection to the last child. > a[name] selects all immidiate children of type a with the attribute name.
The second line just gets the value from the name attribute of the node.
You should get the correct element with that, but I haven't tested it.
Have a look at the dojo reference, there are tons of useful functions.
(I don't work at dojo, I just really like it ;) )
Info
http://docs.dojocampus.org/dojo/query
EDIT
To make sure that you get only the node you want (if you add another link with a name attr), you could add a class "thisisthelinkiwant" (or similar ;) ) to the appropiate link and updating the query to 'Comments .thisisthelinkiwant:last-child'.
You might consider reading about css selectors, as they are quite important with this function.