I'm editing a web page that has a list of doctors names and images wrapped in a div. I'm adding more to that list and my client wants all of the names in alphabetical order now. As apposed to manually doing that (I know my client will also be adding more doctors in the future)I tried writing a script to do the work for me. I wrapped each doctor in a div called "alphabetize," and set a span id of "lastName" around each doctor's last name.
<div class="alphabetize large-6 columns sameheight-wrap">
<div class="large-4 medium-4 columns sameheight PadT20"> <img src="../imgs/dev/crna-staff/John-Doe.jpg" alt=" John Doe, CRNA" class="pictureBottomBorder"> </div>
<div class="large-8 medium-8 columns contenttable sameheight PadT20">
<div class="border vmid text-center bgdblue PadB"> <span class="white medium"><strong>John<span id="lastName">Doe</span></strong><br>
</span> <span class="white PadT5"> MSN, Thomas Jefferson University School of Nurse Anesthesia</span> </div>
</div>
</div>
I placed the following script on that page;
<script>
var $divs = $("div.alphabetize");
$(function() {
var alphabeticallyOrderedDivs = $divs.sort(function (a, b) {
return $(a).find("#lastName").text() > $(b).find("#lastName").text();
});
$("#alpha").html(alphabeticallyOrderedDivs);
});
</script>
For some reason, the script is not working correctly. Doctors are out of order and i also need to add a variable to the code that sorts the last names with the first 3 letters. Can anyone help? Javascript is not my strong suit. Not sure if I missed something.
Below is a snippet that will show you how you can easily sort this. The major issue, however, is the following:
return $(a).find("#lastName").text() > $(b).find("#lastName").text();
The sort() function asks to return one of three values, 0 to maintain position, -1 to move it before the current element and 1 to move it after. That means that all you could ever return is after and not before, so your sort fails.
For the solution I would like to suggest using a data-attribute and no more HTML spans and styles that need to be rendered (and probably hidden afterwards), so here is my suggestion:
<div data-alphabetize="John Doe">John Does Content</div>
We can string together a couple of functions to get the correct output. We will need prototype.slice.call to convert the returned-by-querySelector NodeList to an Array, then we need to use sort to sort it alphabetically and finally we can use forEach to go through the array and insert the nodes in the correct position.
I am using vanilla JS - mostly to show how simple things can be done without loading up jQuery. You can, of course, do this with jQuery as well.
// Turn querySelectorAll NodeList into an Array
Array.prototype.slice.call(document.querySelectorAll('[data-alphabetize]'))
// Sort the array by data-alphabetize attribute (reverse order)
.sort(function(a, b){
return a.getAttribute('data-alphabetize') < b.getAttribute('data-alphabetize')
? 1 : -1;
})
// Insert every node in order
.forEach(function(v, i, a){
var parent = v.parentNode;
parent.removeChild(v);
parent.insertBefore(v, parent.childNodes[0]);
});
<div>
<div data-alphabetize="Beta">Joseph Alfred <strong>Beta</strong></div>
<div data-alphabetize="Alpha">Mark Unicode <strong>Alpha</strong></div>
<div data-alphabetize="Gamma">Graham <strong>Gamma</strong>-Python</div>
<div data-alphabetize="Omega">Matthew <strong>Omega</strong></div>
</div>
Related
I have some HTML code where at the most nested level there is some text I'm interested in:
<div class="main">
<div class="container">
<div class="output_area">
<pre>WHITE 34</pre>
</div>
<div class="output_area">
<pre>RED 05</pre>
</div>
<div class="output_area">
<pre>WHITE 16</pre>
</div>
<div class="output_area">
<pre>BLACK</pre>
</div>
</div>
</div>
What I need to do is I need to return the output_area elements only when their nested <PRE> element contains a word + a number (for example WHITE 05, and not just BLACK).
So this is what I did:
I made an array from all output_area elements:
output_areas = Array.from(document.getElementsByClassName('output_area'));
I filtered the output_areas array to only return those output_area elements whose nested <PRE> satisfies my condition of a word + a number, using a regexp, like so:
output_areas.filter(el => el.textContent.match(/^WHITE \d+$/g));
Now, what happens is this function will only return the first matching result, so I will get an object of length 1 containing just :
<div class="output_area">
<pre>WHITE 34</pre>
</div>
and the output_area element containing <PRE> with "WHITE 16" is not returned.
As you can see at the end of the regular expression I put a "g" to request a global search and not just stop at the first result.
Not understanding why this did not work, I tried to verify what would happen if I would use includes() to perform a search:
output_areas.filter(el => el.textContent.includes('WHITE')
(let's just forget about the numbers now, it's not important)
And what happens? This will also return only the first output_area...
But why??? What am I doing wrong?
I am not ashamed to say I've been banging my head on this for the last couple of hours... and at this point I just want to understand what is not working.
The only clue I think I got is that if I simplify my search using just a == or !=, for example:
output_areas.filter(el => el.textContent != "")) // return all not empty elements
I get back all output_area elements and not just the first one!
So I suspect there must be some kind of problem when using together filter() & match(), or filter() & includes(), but with relation to that my google searches did not take me anywhere useful...
So I hope you can help!
You should use trim here to remove space before and after the text
output_areas.filter( el => el.textContent.trim().match( /^WHITE \d+$/g ))
const output_areas = Array.from(document.getElementsByClassName('output_area'));
const result = output_areas.filter(el => el.textContent.trim().match(/^WHITE \d+$/g));
console.log(result);
<div class="main">
<div class="container">
<div class="output_area">
<pre> WHITE 34 </pre>
</div>
<div class="output_area">
<pre> RED 05 </pre>
</div>
<div class="output_area">
<pre> WHITE 16 </pre>
</div>
<div class="output_area">
<pre> BLACK </pre>
</div>
</div>
</div>
Answering myself as for some reason it then begin to work without any changes from my side... Yes, just one of those typical IT cases we all know... :)
Jokes aside, I think for some reason the webpage (the DOM) got stuck...
Probably the Jupyter Runtime (which was serving the page) had crashed without me noticing, and this caused somehow the kind of inconsistency I was looking at.
Moral of the story: if you see weird behaviour in the interaction with a Python Notebook, always go check the Jupyter Runtime status before getting stupid at trying to fix impossible errors.
I'm not sure what the issue with the Jupyter notebooks is, but generally speaking - based only on the HTML in the question - what I believe you are trying to do can be achieved using xpath instead of css selectors:
html = `[your html above]
`
domdoc = new DOMParser().parseFromString(html, "text/html")
const areas = domdoc.evaluate('//div[contains(./pre," ")]', domdoc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (let i = 0; i < areas.snapshotLength; i++) {
console.log(areas.snapshotItem(i).outerHTML)
}
The output should be the 3 divs meeting the condition.
Just wondered how I would search for all the ids starting with "content_" in the whole page and also a way to only find them in a named div called "extra_content". Once i have all the ids i want to hide them.
Below is an example of what i want to find.
<div id="content_1"></div> <-- Find
<div id="content_2"></div> <-- Find
<div id="contet_3"></div>
<div id="extra_content">
<div id="content_extra_1"></div> <-- Find
<div id="content_extra_2"></div> < -- Find
</div>
Examples would be helpful.
Thanks
Use the attribute-starts-with selector:
$('[id^="content_"]').hide();
To limit the search to elements within extra_content:
$('#extra_content [id^="content_"]').hide();
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;
};
}
I have implemented jQuery masonry to our site and it works great. Our site is dynamic and users must be able to add/remove masonry box's. The site has an add example but no remove example. Our db is queried returning x number of items. Looping through they are loaded and displayed. Here's a code sample: (we are use F3 framework and the F3:repeat is it's looping mechanism.).
<div id="container" class="transitions-enabled clearfix" style="clear:both;">
<F3:repeat group="{{#productItems}}" value="{{#item}}">
<div id="{{#item.itemId}}">
<div class="box">
<div class="view"> <!-- for css -->
<a onclick='quickRemove("{{#item.itemId}}")>
<img src="{{#item.pic}}" />
</a>
</div>
<p>
{{#item.title}}
</p>
</div>
</div>
</F3:repeat>
</div>
In the javascript code the item id number is unique and is passed into the function. It's also the div id# to distinguish each box. I've tried various combinations and methods but can't seem to get this to work.
function quickRemove(item){
var obj = $('#'+item+'').html(); // item is the product id# but also the div id#
$('#container').masonry('remove',obj);
$('#container').masonry('reloadItems');
$('#container').masonry('reload');
}
Has anyone out there successfully removed an item and how did you do it?
Thx.
Currently you appear to be passing a string full of html to the masonry remove method. Pass it the actual jQuery wrapped element by not including .html()
function quickRemove(item){
var obj = $('#'+item+''); // item is the product id# but also the div id#
$('#container').masonry('remove',obj);
$('#container').masonry('reloadItems');
$('#container').masonry('reload');
}
I'm trying to sort some DIVS in different ways and I'm quite lost. I've been trying some stuff but I don't see how to get it to work. I have div data in following format. I have a dropdown with sorting options like sort by price, by distance and by creation date etc.. On selecting an optin from dropdown the divs data should be sorted and dispalyed accordingly. Example is I choose sort by price then data should be displayed in sorted order as with price starting from lower to higher.
I need your guidance on this.
<div id="contentContainer">
<div id="content">
<div>
<div class="price">120</div>
<div class="dateDiv">2012-05-09 20:39:38.0</div>
<div class="distance">20 mile</div>
</div>
<div>
<div class="price">123</div>
<div class="dateDiv">2012-05-10 20:39:38.0</div>
<div class="distance">30 mile</div>
</div>
<div>
<div class="price">100</div>
<div class="dateDiv">2012-05-11 20:39:38.0</div>
<div class="distance">50 mile</div>
</div>
<div>
<div class="price">124</div>
<div class="dateDiv">2012-05-12 20:39:38.0</div>
<div class="distance">60 mile</div>
</div>
</div>
</div>
An example to sort by price:
$('#content div.price').map(function () {
// map sort-value and relevant dom-element for easier handling
return {val: parseFloat($(this).text(), 10), el: this.parentNode};
}).sort(function (a, b) {
// a simple asc-sort
return a.val - b.val;
}).map(function () {
// reduce the list to the actual dom-element
return this.el;
}).appendTo('#content'); // < inject into parent node
demo: http://jsfiddle.net/QmVsD/1/
a few notes:
the first map isn't really needed, but it makes the sorting callback much simpler.
you would need to supply different compare-callbacs for different data-types (e.g. dates, strings)
What you've got there is actually a TABLE, so use a table and one of the existing table sorters.
There is nothing wring with using <table> when you have tabular data, and that's what you've got.
The generated HTML is part of the presentation, while the sorting operation is part of the data model. You should separate these two concepts in your code.
In your case, store the data in arrays (data model). When you want to display the data to user only then lay it out in divs or table html. Do the sorting before you lay out the html. When the user chooses sorting option from the dropdown, empty the #contentContainer, sort the array of data, regenerate html of newly ordered data and insert it into #contentContainer.
I just tested this and it worked fine for me. Youll just decide what you do with your array afterwards. :)
$(document).ready(function () {
var priceItems = $('.price');
var arr = new Array();
for (var i = 0; i < priceItems.length;i++) {
var tempInt = priceItems[i].innerHTML;
tempInt = parseInt(tempInt);
arr.push(tempInt);
}
arr.sort()
});
All you now need, is use your array.