I ran into a problem of targeting different nested levels.
I read that it is possible to use .children feature + for loop, but I failed to do that.
Let's say I want to have a function where you pass the nesting level and it will change some property of <li> element on that level only.
I wrote this function to add classes to all last <li>
function changeElement(){
var lastLi = document.querySelectorAll('li:last-child');
for(i = 0; i < lastLi.length; i++){
lastLi[i].classList.add('last');
lastLi[i].style.background = 'green';
}
}
But now I need to target <li> elements on specific nested level
function changeElementOnLevel(level) {
var lastLi = document.querySelectorAll('li:last-child');
for (i = 0; i < lastLi.length; i++) {
//if level is 1 than we should change ul.root > li:last-child
//if level is 3 than we should change ALL <li:last-child> inside ul.root
> li > ul >
}
}
changeElementOnLevel(3)
<ul class='root'>
<li>Cat
<ul>
<li>Eats1</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Meows</li>
</ul>
</li>
<li>Dog
<ul>
<li>Eats2</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Barks</li>
</ul>
</li>
<li>Fish
<ul>
<li>Eats3</li>
<li>Sleeps</li>
<li>Swims</li>
<li>Plays</li>
<li>Swims</li>
</ul>
</li>
I consider that you have some nested ul tags, so in your example, the ul.root is level 1 and the inner ul is level 2 and if instead of 'Eats1' you have a ul tag, it will be level 3 and ...
Use this function:
function changeElementOnLevel(level){
let query = 'ul.root>li';
for (let i = 1; i < level; i++) {
query += '>ul>li'
}
query += ':last-child'
var lastLi = document.querySelectorAll(query);
for(i = 0; i < lastLi.length; i++){
// your code
}
}
It's depends what exactly you want to do. Are you going to change inner HTML on click? What do you mean by saying third level? You have 3 levels, and 3 lists. More information needed in order to help you.
var myElement = document.getElementsByClassName('root');
var arrayOfPets = myElement[0].children; // we are getting first level, all animals
var secondLevelArr = [];
for(i = 0; i < arrayOfPets.length; i++){
arrayOfPets[i].classList.add('last');
arrayOfPets[i].style.background = 'green';
var secondLevel = arrayOfPets[i].children[0].children;
// Push results to array
secondLevelArr.push(secondLevel);
} // will add class and background to all of them
// To add styles only to last element, you do not need to loop through them
arrayOfPets[arrayOfPets.length - 1].style.background = 'red';
for(i = 0; i < secondLevelArr.length; i++){
secondLevelArr[i][0].style.color = "white";
for(j = 0; j < secondLevelArr[i].length; j++){
secondLevelArr[i][j].style.textDecoration = 'line-through';
}
secondLevelArr[i][secondLevelArr[i].length - 1].style.textDecoration = 'none';
}
<ul class='root'>
<li>Cat
<ul>
<li>Eats1</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Meows</li>
</ul>
</li>
<li>Dog
<ul>
<li>Eats2</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Barks</li>
</ul>
</li>
<li>Fish
<ul>
<li>Eats3</li>
<li>Sleeps</li>
<li>Swims</li>
<li>Plays</li>
<li>Swims</li>
</ul>
</li>
</ul>
Here's what I came up with on the fly:
First, I'd make a small tweak to your HTML (I'm not sure what you have is actually valid...it might display how you like, but structurally it's going to cause problems). It is going to be difficult to set the value of an "li", if it has the value AND a nested list. If you reset the innerText or the innerHtml, you're going to completely overwrite the rest of the HTML in that tag, meaning you'll lose the nested list. You could work around this, but why bother, just close those tags predictably.
(Note I don't think any of the other answers address this issue).
So I'd first do this, notice how I close the "li" for Cat, Dog, and Fish:
<ul class='root'>
<li>Cat</li>
<ul>
<li>Eats1</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Meows</li>
</ul>
<li>Dog</li>
<ul>
<li>Eats2</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Barks</li>
</ul>
<li>Fish</li>
<ul>
<li>Eats3</li>
<li>Sleeps</li>
<li>Swims</li>
<li>Plays</li>
<li>Swims</li>
</ul>
</ul>
Now you can select elements and set values very straightforwardly (and the HTML is sound); the selectors here basically say "give me the li and ul children that are ONLY direct descendants of whatever element I'm currently working with" (otherwise you will get all of them no matter how deeply nested, which you don't want).
This code gets you the desired result by working recursively on nested "li" and "ul" collections, note that it also works on the top level "li" collection:
const top_layer = document.querySelectorAll ( '.root' );
const the_new_val = 'THE NEW VAL';
function setProps ( elems, level ) {
Array.from ( elems ).forEach ( x => {
const li = x.querySelectorAll ( ':scope > li' );
const ul = x.querySelectorAll ( ':scope > ul' );
if ( Array.from ( li ).length >= level ) {
li [ level ].innerText = the_new_val;
setProps ( li [ level ].children, level );
}
if ( Array.from ( ul ).length ) {
setProps ( ul, level );
}
});
}
setProps ( top_layer, 2 );
Yes, you could work with "children", but since we are directly interested in setting "li" values, which always appear in "ul" tags, the explicit selectors make it more obvious what's going on and would ignore any other children that may be around, feel free to make that change if you like.
The displayed result:
It is not very clear what you are trying to achieve :) But you can try :nth-child() - CSS pseudo-class selector that allows you to select elements based on their index (source order) inside their container.
This is just an example:
function find(n) {
// returns NodeList
var liNodeList = document.querySelectorAll('li:nth-child(' + n + ')');
console.log(li);
// if you need to do something with those elements, you can iterate
for (var i = 0; i < liNodeList.length; ++i) {
var item = liNodeList[i];
// do what you need with particular item
}
}
Also, the right method is querySelectorAll(...). What you are using querySelectAll does not exist.
Try like below,
I had used mix of query selectors and traversal to achieve this,
function changeElementOnLevel(level) {
var rootElement = document.querySelector(".root");
let targetLi;
if (level === 1) {
targetLi = rootElement.children[rootElement.children.length - 1];
let ul = targetLi.querySelector("ul"); // Since a ul is also there in level 1, I am expecting we need to change only li value not the ul
targetLi.textContent = "changed Value";
targetLi.append(ul);
} else if (level === 3) {
targetLi = rootElement.querySelector(
"li:last-child ul li:last-child"
);
targetLi.textContent = "changed Value";
}
}
changeElementOnLevel(3);
<ul class="root">
<li>
Cat
<ul>
<li>Eats1</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Meows</li>
</ul>
</li>
<li>
Dog
<ul>
<li>Eats2</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Barks</li>
</ul>
</li>
<li>
Fish
<ul>
<li>Eats3</li>
<li>Sleeps</li>
<li>Swims</li>
<li>Plays</li>
<li>Swims</li>
</ul>
</li>
</ul>
This code resolved my issue:
var parent = document.querySelector('.root');
function setFirstItemClass(element, level){
level = +level;
if(level == 1){
console.dir(element);
if(element.children.length > 0){
element.children[0].style.backgroundColor = 'red';
}
} else {
if(element.children.length > 0){
level--;
for(child of element.children){
setFirstItemClass(child, level);
}
}
}
}
setFirstItemClass(parent, 3);
Is it possible to lock list items in JQuery sortable list in a way that those items will stay in that particular place in the list.
For example,
consider this pseudo list with locked items...
item A
item B(locked)
item C(locked)
item D
item E
item F
item G(locked)
So, I'd like to have the items B,C and G to be fixed in a way that if user drag and drop item D at the start of the list, the item A "jumps" over fixed/locked items B and C with following results...
item D
item B(locked)
item C(locked)
item A
item E
item F
item G(locked)
I've been searching for something like this without luck. Is it possible..?
Here's a hopefully bug-free version, updating as you drag. It's generating the current desired positions of the items when sorting starts, which means you should be able to change the classes whenever you need, refresh the widget's list items and you're good to go.
It also uses the sortable's built-in items property to prevent dragging the fixed items and to sort out any sorting problems at the top and the bottom of the list.
I tried to move the fixed items around, but that resulted in horribly buggy behaviour, especially when there are multiple fixed items in groups. The final solution detaches all fixed items from the list, adds a helper element to the front, then re-inserts the fixed elements to their desired position, which seems to fix all bugs.
Try the demo here: http://jsfiddle.net/PQrqS/1/
HTML:
<ul id="sortable">
<li>oranges</li>
<li class="static">apples</li>
<li>bananas</li>
<li>pineapples</li>
<li>grapes</li>
<li class="static">pears</li>
<li>mango</li>
</ul>
CSS:
.static { color:red; }
li { background-color:whitesmoke; border:1px solid silver; width:100px; padding:2px; margin:2px; }
Javascript:
$('#sortable').sortable({
items: ':not(.static)',
start: function(){
$('.static', this).each(function(){
var $this = $(this);
$this.data('pos', $this.index());
});
},
change: function(){
$sortable = $(this);
$statics = $('.static', this).detach();
$helper = $('<li></li>').prependTo(this);
$statics.each(function(){
var $this = $(this);
var target = $this.data('pos');
$this.insertAfter($('li', $sortable).eq(target));
});
$helper.remove();
}
});
I extended the jQuery.Ui.sortable:
Overview
jQuery.Ui.sortable widget extension with fixed feature. This feature allows user to fix elements in the list.
With the .fixedsortable() constructor you construct a .sortable() class which extended with the features. You can use the original methods and the extended as well.
Code
https://gist.github.com/3758329#file_fixedsortable.js > fixedsortable.js
Example
http://jsfiddle.net/omnosis/jQkdb/
Usage
General:
To use, add the fixed property to the sortable list optios:
$("#list").fixedsortable({
fixed: (value)
})
the value can be:
integer example: 3
array of integers example : [1,2,5]
a html element or a list of html elements
a css selector
jquery object
HTML:
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script> //the jquery
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/jquery-ui.min.js"></script> //the original jquery-ui
<script type="text/javascript" src="https://raw.github.com/gist/3758329/91749ff63cbc5056264389588a8ab64238484d74/fixedsortable.js"></script> //the extended sortable
...
<ul id="sortable1">
<li>oranges</li>
<li class="static">apples</li>
<li>bananas</li>
<li>pineapples</li>
<li>grapes</li>
<li class="static">pears</li>
<li>mango</li>
</ul>
<ul id="sortable2">
<li>bananas</li>
<li foo="asd">oranges</li>
<li foo="dsa">apples</li>
<li>pineapples</li>
<li>grapes</li>
<li>pears</li>
<li>mango</li>
</ul>
<ul id="sortable3">
<li>bananas</li>
<li>oranges</li>
<li>apples</li>
<li>pineapples</li>
<li>grapes</li>
<li>pears</li>
<li>mango</li>
</ul>
Javascript
$(function() {
$("#sortable1").fixedsortable({
fixed: "> .static"
});
$("#sortable2").fixedsortable({
fixed: $("li[foo]").css("background","red")
});
$("#sortable3").fixedsortable({
fixed: 2
})
});
Notes:
If you insist to use the .sortable instead of .fixedsortable you can use this https://gist.github.com/3758329#file_sortable.js instead of the jquery.ui library. This is a complete replacement of the jQuery.ui, but i don't recommend to use this because of later updates.
i have been working on this more than 12 hours :( i am insane..
Check this out: Forcing an item to remain in place in a jQuery UI Sortable list
Also, I've implemented the above solution with multiple fixed elements here: http://jsfiddle.net/enBnH/12/ (obsolete, see below)
It's rather self-explanatory, i think.
EDIT:
I've automated the process for generating the lockto values as well as adding ID's to those lis with the class "fixed" (note that i have to add an ID so we can reference it)
See the COMPLETE solution HERE: http://jsfiddle.net/enBnH/44/
EDIT
Okay, after a gazillion errors with the above, i just rewrote the dang thing myself:
http://jsfiddle.net/thomas4g/GPMZZ/15/
NOTE: The above does work, but #DarthJDG's answer seems a lot nicer to me. I'm leaving mine up on the offchance someone might prefer how mine behaves (i've learned not to delete stuff just beceause there's a better version :P )
Using the items parameter you can achieve what you want like this:
$("#mytable tbody").sortable({items: 'tr.sortable'});
Only rows with a .sortable CSS class can be sorted now.
If you want to lock only the 1st row you can do this:
$("#mytable tbody").sortable({items: 'tr:not(:first)'});
The possibilities are endless...
This is based on #DarthJDG code. However it wasn't retrieving all the id's and the sorting wasn't working with the table. So I managed to update his solution which works with both list and tables and keeps the id in the array.
Javascript:
var fixed = '.static'; //class which will be locked
var items = 'li'; //tags that will be sorted
$('ul').sortable({
cancel: fixed,
items: items,
start: function () {
$(fixed, this).each(function () {
var $this = $(this);
$this.data('pos', $this.index());
});
},
change: function () {
var $sortable = $(this);
var $statics = $(fixed, this).detach();
var tagName = $statics.prop('tagName');
var $helper = $('<'+tagName+'/>').prependTo(this);
$statics.each(function () {
var $this = $(this);
var target = $this.data('pos');
$this.insertAfter($(items, $sortable).eq(target));
});
$helper.remove();
}
});
Demo: http://plnkr.co/edit/hMeIiRFT97e9FGk7hmbs
Connected sortables and fixed items
I ran into the problem when we have several connected sortables. The code suggested by #sarunast and #DarthJDG has erroneous behavior when dragging items from one list to another.
Therefore, I have modified it a little, and now you can drag items from different lists with saving positions in both of them.
javascript:
let connected = '.soratble';
let fixed = '.static';
let newParentContainer;
//wrap the code suggested by #sarunast and #DarthJDG into the function
//code was modified a little
function sortingAroundFixedPositions(container) {
let sortable = $(container);
let statics = $(fixed, container).detach();
let tagName = statics.prop('tagName');
let helper = $('<' + tagName + '/>').prependTo(container);
statics.each(function() {
let target = this.dataset.pos;
let targetPosition = $(tagName, sortable).eq(target);
if (targetPosition.length === 0) {
targetPosition = $(tagName, sortable).eq(target - 1)
}
$(this).insertAfter(targetPosition);
});
helper.remove();
}
$('ul').sortable({
connectWith: connected,
cancel: fixed,
start: function() {
$(fixed, connected).each(function() {
this.dataset.pos = $(this).index();
});
},
change: function(e, ui) {
sortingAroundFixedPositions(this);
if (ui.sender) {
newParentContainer = this;
}
if (newParentContainer) {
sortingAroundFixedPositions(newParentContainer);
}
},
update: function(e, ui) {
newParentContainer = undefined;
}
});
demo: http://plnkr.co/edit/blmv4ZjaWJFcjvO2zQH0
Just use the "Include/Exclude" items selectors.
Here is the link: https://jqueryui.com/sortable/#items
oh no! gist link is broken. here is code dump from https://gist.github.com/peterh-capella/4234752
code accessed Jan 6, 2016
//this code is created to fix this problem: http://stackoverflow.com/questions/4299241/
(function( $, undefined ) {
$.widget("ui.fixedsortable", $.ui.sortable, {
options: $.extend({},$.ui.sortable.prototype.options,{fixed:[]}),
_create: function() {
var o = this.options;
this.containerCache = {};
this.element.addClass("ui-sortable");
//Get the items
$.ui.sortable.prototype.refresh.apply(this,arguments);
if( typeof this.options.fixed == "number") {
var num = this.options.fixed
this.options.fixed = [num];
}
else if( typeof this.options.fixed == "string" || typeof this.options.fixed == "object") {
if(this.options.fixed.constructor != Array) {
var selec = this.options.fixed;
var temparr = [];
var temp = $(this.element[0]).find(selec);
var x = this;
temp.each(function() {
var i;
for(i=0;i<x.items.length && x.items[i].item.get(0) != this;++i) {}
if(i<x.items.length) temparr.push(i);
});
this.options.fixed = temparr;
}
}
//Let's determine if the items are being displayed horizontally
this.floating = this.items.length ? o.axis === 'x' || (/left|right/).test(this.items[0].item.css('float')) || (/inline|table-cell/).test(this.items[0].item.css('display')) : false;
//Let's determine the parent's offset
this.offset = this.element.offset();
//Initialize mouse events for interaction
$.ui.sortable.prototype._mouseInit.apply(this,arguments);
},
_mouseCapture: function( event ) {
this._fixPrev = this._returnItems();
return $.ui.sortable.prototype._mouseCapture.apply(this,arguments);
},
_mouseStart: function( event ) {
for(var i=0;i<this.options.fixed.length;++i) {
var num = this.options.fixed[i];
var elem = this.items[num];
if(event.target == elem.item.get(0)) return false;
}
return $.ui.sortable.prototype._mouseStart.apply(this,arguments);
},
_rearrange: function(event, i, a, hardRefresh) {
a ? a[0].appendChild(this.placeholder[0]) :
i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction == 'down' ? i.item[0] : i.item[0].nextSibling));
this._refix(i);
//Various things done here to improve the performance:
// 1. we create a setTimeout, that calls refreshPositions
// 2. on the instance, we have a counter variable, that get's higher after every append
// 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
// 4. this lets only the last addition to the timeout stack through
this.counter = this.counter ? ++this.counter : 1;
var self = this, counter = this.counter;
window.setTimeout(function() {
if(counter == self.counter) self.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
},0);
},
_refix: function(a) {
var prev = this._fixPrev;
var curr = this._returnItems();
var Fixcodes = this.options.fixed;
var NoFixed = [];
var Fixed = [];
var Mixed = []
var post = [];
for(var i=0;i<Fixcodes.length;++i) {
var fix_index = Fixcodes[i];
var fix_item = prev[fix_index];
var j = 0;
for(j=0;j<curr.length && curr[j].item.get(0) != fix_item.item.get(0);++j) {}
curr.splice(j,1);
Fixed.push(fix_item);
}
for(var i=0;i<curr.length;++i) {
if(curr[i].item.get(0) != this.currentItem.get(0)) {
NoFixed.push(curr[i]);
}
}
var fix_count = 0;
var nofix_count = 0;
for(var i=0;i<Fixed.length + NoFixed.length;++i) {
if(Fixcodes.indexOf(i) >= 0) {
Mixed.push(Fixed[fix_count++]);
}
else {
Mixed.push(NoFixed[nofix_count++]);
}
}
var parent = this.currentItem.get(0).parentNode;
var allchild = parent.children;
for(var i=0;i<Mixed.length;++i) {
parent.removeChild(Mixed[i].item.get(0));
parent.appendChild(Mixed[i].item.get(0));
}
},
_returnItems: function(event) {
this.containers = [this];
var items = [];
var self = this;
var queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]];
var connectWith = $.ui.sortable.prototype._connectWith.apply;
if(connectWith) {
for (var i = connectWith.length - 1; i >= 0; i--){
var cur = $(connectWith[i]);
for (var j = cur.length - 1; j >= 0; j--){
var inst = $.data(cur[j], 'sortable');
if(inst && inst != this && !inst.options.disabled) {
queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
this.containers.push(inst);
}
};
};
}
for (var i = queries.length - 1; i >= 0; i--) {
var targetData = queries[i][1];
var _queries = queries[i][0];
for (var j=0, queriesLength = _queries.length; j < queriesLength; j++) {
var item = $(_queries[j]);
item.data('sortable-item', targetData); // Data for target checking (mouse manager)
items.push({
item: item,
instance: targetData,
width: 0, height: 0,
left: 0, top: 0
});
};
};
return items;
},
value: function(input) {
//console.log("test");
$.ui.sortable.prototype.value.apply(this,arguments);
}
});
})(jQuery);
And dumping rest of his answer, just in case
dependencies
https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js
https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/jquery-ui.min.js
Script
function randomColor() { //for a little fun ;)
var r = (Math.floor(Math.random()*256));
var g = (Math.floor(Math.random()*256));
var b = (Math.floor(Math.random()*256));
return "#" + r.toString(16) + g.toString(16) + b.toString(16)
}
$(function() {
$("#sortable1").fixedsortable({
fixed: "> .static", //you can use css selector
sort: function() { //you can add events as well, without getting confused. for example:
$(".static").css("background",randomColor()) //change the fixed items background
},
change: function(event,ui) {
$(ui.item[0]).css("border","2px solid "+randomColor()) //change the captured border color
},
stop: function(event,ui) {
$(ui.item[0]).css("border","2px solid #777"); //change the back the css modifications
$("#sortable1 > li.static").css("background","#aaa");
}
});
$("#sortable2").fixedsortable({ //you can use jQuery object as selector
fixed: $("li[foo]").css("background","red")
});
$("#sortable3").fixedsortable({
fixed: [2,4], //you can use array of zero base indexes as selector
update: function(event, ui) {
alert($(this).fixedsortable('toArray')) //the fixedsortable('toArray') also works
}
})
$("#sortable4").fixedsortable({
fixed: 5 //you can fix a single item with a simple integer
})
});
HTML
<body>
<div style="width:120px;float:left;">
<ul id="sortable1">
<li>oranges</li>
<li class="static">apples</li>
<li>bananas</li>
<li>pineapples</li>
<li>grapes</li>
<li class="static">pears</li>
<li>mango</li>
</ul>
<ul id="sortable2">
<li>bananas</li>
<li foo="asd">oranges</li>
<li foo="dsa">apples</li>
<li>pineapples</li>
<li>grapes</li>
<li>pears</li>
<li>mango</li>
</ul>
</div>
<div style="width:120px;float:left;">
<ul id="sortable3">
<li id="fru_1">bananas</li>
<li id="fru_2">oranges</li>
<li id="fru_3" style="background:#f4f">apples</li>
<li id="fru_4">pineapples</li>
<li id="fru_5" style="background:#faaba9">grapes</li>
<li id="fru_6">pears</li>
<li id="fru_7">mango</li>
</ul>
<ul id="sortable4">
<li>bananas</li>
<li>oranges</li>
<li>apples</li>
<li>pineapples</li>
<li>grapes</li>
<li style="background:#dada00">pears</li>
<li>mango</li>
</ul>
</div>
</body>
CSS
ul {margin:10px;}
ul#sortable1 > li, ul#sortable2 > li, ul#sortable3 > li, ul#sortable4 > li {
display:block;
width:100px;
height:15px;
padding: 3px;
background: #aaa;
border: 2px solid #777;
margin: 1px;
}
ul#sortable1 > li.static {
opacity:0.5;
}
Maybe this will help to someone: use methods "disable" and "enable". Example
HTML:
<ul class="sortable">
<li>You can move me</li>
<li data-state="lifeless">You can't move me.</li>
</ul>
Script:
$('#sortable').sortable();
$('#sortable').mousedown(function() {
if($(this).data('state')=='lifeless') $('#sortable').sortable('disable');
else $('#sortable').sortable('enable');
});
Live example here: https://jsfiddle.net/ozsvar/0ggqtva5/2/
There is a slightly better way to solve this.
You need to use a grid instead of a list and then you can fix the position of an element by declaring where the element should be arranged with css:
.fixed-element {
grid-column-start: 1;
grid-column-end: 1;
grid-row-start: 1;
grid-row-end: 1;
}
I have a list of items that I want to be inside multiple columns. These columns can be arbitrary based on a series of factors so there's some other stuff going on. Unfortunately I'm getting tricked up on how to close the containing div.
Let's say I have a structure like this:
<div class="col-md-4 JS-columnGrid">
<li>one</li>
<li>two</li>
<li>three</li>
<!--</div>-->
<!--<div class="col-md-4">-->
<li>four</li>
</div>
I want to close the col-md-4 div after the third list element, and wrap the fourth list element in another div class="col-md-4">
This would create two columns.
Here's some javascript that would grab each list, throw it into an array and then my intention is that I can use that array to decide where I'm going to cut the column.
var cutoffArray = [];
$(".JS-columnGrid > li").each(function(){
cutoffArray.push(this);
console.log(cutoffArray);
});
$(cutoffArray[3]).before("</div><div class='col-md-3'>");
Unfortunately this is the output HTML after this:
<div class="col-md-4 JS-columnGrid">
<li>one</li>
<li>two</li>
<li>three</li>
<div class="col-md-3"></div>
<li>four</li>
</div>
Where am I going wrong?
// select and remove all the elements you want to re-package
var $lis = $(".col-md-4.JS-columnGrid li").remove(),
i,
divs = [];
$(".outer-container").empty();
// slice into packs of three and append each pack to container
for (i = 0; i < $lis.length; i += 3) {
$("<div class='col-md-3'></div>")
.append( $lis.slice(i, i + 3) )
.appendTo(".outer-container");
}
You can try this:
$(function(){
var cutoffArray = [],
$col = $('<div class="col-md-3"></div>');
$(".JS-columnGrid > li").each(function(){
cutoffArray.push(this);
});
$('.JS-columnGrid').after($col);
$(cutoffArray[3]).appendTo($col);
});
Working fiddle
Updated answer:
if you want to add more li's change the above to this:
var $col = $('<div class="col-md-3"></div>');
$('.JS-columnGrid').after($col);
$(".JS-columnGrid > li").each(function(index){
if(index > 2) {
$col.append($(this));
}
});
updated fiddle
despite the fact that you have <li> elements in the "wrong" place, browsers wont just ignore them and you can still manipulate them
var div = document.querySelector('.col-md-4.JS-columnGrid');
var lis = div.querySelectorAll('li');
var newDiv = document.createElement('div');
newDiv.className = 'col-md-3';
for (var i = 3; i < lis.length; i++) {
newDiv.appendChild(lis[i]);
}
div.parentElement.insertBefore(newDiv, div.nextElementSibling);
That will do what you want
You can't use "</div><div>" in .before() etc, since the argument is expected to be one or more properly formatted HTML elements (or plain text). You're working on the DOM here, not plain text, and the textual HTML representation of the argument is purely convenience.
You could instead simply move the elements:
var els = $('.JS-columnGrid > li');
var x = $( "<div class='col-md-4 JS-columnGrid'></div>" );
els.parent().after( x );
for (var i = 0; i < els.length; i ++ )
if ( i >= 3 )
x.append( els[i] );
We have:
<ul>
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
</ul>
How do I get second half of this list and move it before first <li>?
var ul = $('ul');
var lis = ul.children('li');
ul.prepend(lis.slice(Math.round(lis.length / 2)))
Example: http://jsfiddle.net/8SNGn/
This first caches the <ul> element and its <li> children using the children()[docs] method.
Then it uses the prepend()[docs] method to add the last li elements to the beginning of the ul.
You get the last ones by using the slice()[docs] method and passing it the total number of li elements divided by 2 (rounded up using Math.round() for when you have odd number).
EDIT:
I just noticed that your question title has the word "clone" in it.
The rest of the question doesn't seem to suggest it, but if you really wanted to clone the last half, you'd use the clone()[docs] method.
var ul = $('ul');
var lis = ul.find('li');
ul.prepend(lis.slice(Math.round(lis.length / 2)).clone())
Example: http://jsfiddle.net/8SNGn/1/
And if you want the rounding to go the other direction, you'd use Math.floor() instead of Math.round.
var items = $("ul li");
items.filter(function(index) {
return index > items.length / 2;
}).prependTo($("ul")[0]);
Should work.
var len = $('li').length - 1;
for(var i = 0; i < len/2; i++){
var $li = $('li').eq(len);
$('ul').prepend($li);
}
DEMO:
http://jsfiddle.net/NiceGuy4263/Ws5ZK/
jquery prepend http://api.jquery.com/prepend/
Look at jQuery's .slice() filter:
http://api.jquery.com/slice
Ok so I have columns I want added together if there is any information in them. So say I have
Accounts
1
2
3
.
There are 4 account spaces but only 3 accounts.
How do I create java script to add this up.
Live Example
HTML:
<ul>
<li id="accounts">
<p> Accounts </p>
<ul>
<li> 1 </li>
<li> 2 </li>
<li> 3 </li>
<li> . </li>
</ul>
</li>
</ul>
JavaScript:
// Get accounts, ul and set sum to 0
var acc = document.getElementById("accounts"),
ul = acc.getElementsByTagName("ul")[0],
sum = 0;
// Filter out text nodes. Returning an array of <LI> elements
var lis = Array.prototype.filter.call(ul.childNodes, function(li) {
if (li.tagName === "LI") {
return true;
}
});
// Loop through LIs adding up the sum
for (var i = 0, ii = lis.length; i < ii; i++) {
// If it's the last LI element then set the textContent.
if (i === ii - 1) {
lis[i].textContent = sum;
} else {
sum += +lis[i].textContent;
}
}
Disclaimer: Requires Modern Browser or Modernizr.
If your real markup is a list like that, you could do something like this:
// using jquery syntax for brevity; consider it pseudocode
var jList = $('#accounts');
function addColumns() {
var iSum = 0;
jList.find('li').each(function() {
var jLI = $(this);
if(parseFloat(jLI.text()) != 'NaN')
iSum += parseFloat(jLI.text());
});
return iSum;
}
This isn't super-great code. If you give us a little more info about what you're working with, something a little more robust should suggest itself. But the basic idea is that you check a set of elements-of-interest to see if they have summable content (i.e. if their text content can be interpreted as a number); then you add the summable items together.
The requirements of an algorithm like that will impose constraints on the way your "columns" can be marked-up. But there's a near-infinite set of possibilities.