How I can change child elements order in JS? - javascript

I have this html:
<table>
<tr>
<td>
Set right order
</td>
<td>
<span style = "display: block;">asd | ↑↓</span>
<span style = "display: block;">dsa | ↑↓</span>
<span style = "display: block;">qwe | ↑↓</span>
<span style = "display: block;">ewq | ↑↓</span>
</td>
</tr>
</table>
And this JS:
function moveChoiceTo(elem_choice, direction)
{
var curr_index = -1; //index of elem that we should move
var td = elem_choice.parentElement.parentElement; //link to TD
for (var i = 0; i < td.children.length; i++) //determine index of elem that called this function
if (td.children[i].children[0] == elem_choice)
{
curr_index = i;
break;
}
if (curr_index == -1)
return;
if (curr_index == 0 && direction < 0) //if nowhere to move
return;
if (curr_index == td.children.length - 1 && direction > 0) //if nowhere to move
return;
var curr_child = td.children[curr_index]; //save current elem into temp var
td.children.splice(curr_index, 1); //here I getting exception that splice isn't supported by object, but arent this is array?
td.children.splice(curr_index + direction, 0, curr_child); //attempt to insert it
}
I getting exception that splice isn't supported, but this supposed to be an array and support this method?
What other ways I have to change children order?

I will add the answer with simpler (and better) approach:
function moveChoiceTo(elem_choice, direction) {
var span = elem_choice.parentNode,
td = span.parentNode;
if (direction === -1 && span.previousElementSibling) {
td.insertBefore(span, span.previousElementSibling);
} else if (direction === 1 && span.nextElementSibling) {
td.insertBefore(span, span.nextElementSibling.nextElementSibling)
}
}
The key idea is using insertBefore method properly. You also don't need to remove anything from DOM.
Demo: http://jsfiddle.net/dq8a0ttt/

Splice is not supproted in HTMLCollection, you need to use .removeChild and .insertBefore something like this:
var before = td.children[curr_index + direction];
var child = td.children[curr_index];
td.removeChild(child);
td.insertBefore(child, before); //attempt to insert it

Same as dfsq's answer but with some modifications.
var selected_element;
function moveElementTo(selected_element, direction) {
var element_to_move = selected_element,
td = element_to_move.parentNode;
if (direction === -1 && element_to_move.previousElementSibling) {
td.insertBefore(element_to_move, element_to_move.previousElementSibling);
} else if (direction === 1 && element_to_move.nextElementSibling) {
td.insertBefore(element_to_move, element_to_move.nextElementSibling.nextElementSibling)
}
}
function move(dir){
if (selected_element != undefined){
moveElementTo(selected_element,dir);
}else{
console.error("No element Selected to move!");
}
}
<div >
<div onclick='selected_element = this;'><button onclick='selected_element = this.parentNode;'> Select element </button>Element One <button onclick='move(-1);'>Move_up</button></div>
<div onclick='selected_element = this;'><button onclick='selected_element = this.parentNode;'> Select element </button>Element Two <button onclick='move(-1);'>Move_up</button></div>
<div onclick='selected_element = this;'><button onclick='selected_element = this.parentNode;'> Select element </button>Element Three <button onclick='move(-1);'>Move_up</button></div>
</div>
My scenario for this was An element needs to be selected first, because I use it for a popup dialogue for which the operations show up upon long click, the Select Element button is just for demo.

Related

How can I add two JavaScript functions to this HTML element

I am trying to create 'previous' and 'next' buttons that cycle through different locations on an embedded Google Maps image, but have the 'previous' button hidden at the first location, and have the 'next' button disappear once the last location shows.
I know adding a CSS class called 'hidden' would do the trick, however I'm not sure where to place it, and how to place it.
Here is my HTML code. The iframe element is the google maps, and I gave it an id of 'mappy', to select it easier in JavaScript. Below the map are the two buttons.
Here is my JavaScript code. Currently, the next and previous buttons just cycle through each location.
var main_map = document.getElementById('mappy');
var maps = [
'https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d106094.85581787785!2d-118.22632098885458!3d33.80033081467724!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x80c2cae84099d759%3A0xa1003afac42a8faa!2sLong%20Beach%2C%20CA!5e0!3m2!1sen!2sus!4v1598942170840!5m2!1sen!2sus',
'https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d7961373.37326689!2d96.9825121578134!3d13.010860368647212!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x304d8df747424db1%3A0x9ed72c880757e802!2sThailand!5e0!3m2!1sen!2sus!4v1598948055475!5m2!1sen!2sus',
'https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d12436315.314415561!2d166.3019164867038!3d-40.448493377535335!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x6d2c200e17779687%3A0xb1d618e2756a4733!2sNew%20Zealand!5e0!3m2!1sen!2sus!4v1598944334239!5m2!1sen!2sus',
'https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d29616594.651652496!2d115.15555667021819!3d-25.02365472591425!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x2b2bfd076787c5df%3A0x538267a1955b1352!2sAustralia!5e0!3m2!1sen!2sus!4v1598944469264!5m2!1sen!2sus'
];
var i = 0; // Current Image Index
var a;
function prev() {
if (i <= 0) i = maps.length;
i--;
return setImg();
}
function next() {
if (i >= maps.length - 1) i = -1;
i++;
return setImg();
}
function setImg() {
return main_map.setAttribute('src', maps[i]);
}
<p><iframe id="mappy" src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d106094.85581787785!2d-118.22632098885458!3d33.80033081467724!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x80c2cae84099d759%3A0xa1003afac42a8faa!2sLong%20Beach%2C%20CA!5e0!3m2!1sen!2sus!4v1598942170840!5m2!1sen!2sus"
width="600" height="450" frameborder="0" style="border:0;" allowfullscreen="" aria-hidden="false" tabindex="0"></iframe></p>
<div class="buttons">
<button class="button" id="prev" onclick="prev()">← Previous</button>
<button class="button" id="next" onclick="next()">Next →</button>
</div>
You can check the index, if it is 0 make the previous button hide, and if the index of is 4 you make the next button hide. You can use toggleclass to hide the buttons
if (i = 0) { //if index is 0, toggle class hide
classList.toggle("hide");
else {
classList.toggle(“show”); //if the index is not 0, toggle class show
}
}
if (i = 4) { //if index is 4, toggle class hide
classList.toggle("hide");
else {
classList.toggle(“show”); //if the index is not 4, toggle class show
}
}
Edit: added a nested else statement to toggle class show if the index isn’t 0 or 4.
There are definitely better ways to do this, but this is probably the least convoluted method so you can see how it's done and probably rewrite it better!
var main_map = document.getElementById('mappy');
var previousBtn = document.getElementById('btn-prev');
var nextBtn = document.getElementById('btn-next')
var maps = [
'https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d106094.85581787785!2d-118.22632098885458!3d33.80033081467724!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x80c2cae84099d759%3A0xa1003afac42a8faa!2sLong%20Beach%2C%20CA!5e0!3m2!1sen!2sus!4v1598942170840!5m2!1sen!2sus',
'https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d7961373.37326689!2d96.9825121578134!3d13.010860368647212!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x304d8df747424db1%3A0x9ed72c880757e802!2sThailand!5e0!3m2!1sen!2sus!4v1598948055475!5m2!1sen!2sus',
'https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d12436315.314415561!2d166.3019164867038!3d-40.448493377535335!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x6d2c200e17779687%3A0xb1d618e2756a4733!2sNew%20Zealand!5e0!3m2!1sen!2sus!4v1598944334239!5m2!1sen!2sus',
'https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d29616594.651652496!2d115.15555667021819!3d-25.02365472591425!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x2b2bfd076787c5df%3A0x538267a1955b1352!2sAustralia!5e0!3m2!1sen!2sus!4v1598944469264!5m2!1sen!2sus'];
var i = 0; // Current Image Index
var a;
function prev() {
if (i <= 0) i = maps.length;
i--;
viewButtons(i);
return setImg();
}
function next() {
if (i >= maps.length - 1) i = -1;
i++;
viewButtons(i);
return setImg();
}
function viewPrevious(index) {
if (index === 0) {
previousBtn.classList.add('hidden');
} else {
previousBtn.classList.remove('hidden');
}
}
function viewNext(index) {
if (index === maps.length - 1) {
nextBtn.classList.add('hidden');
} else {
nextBtn.classList.remove('hidden');
}
}
function viewButtons(index) {
viewPrevious(index);
viewNext(index);
}
function setImg() {
return main_map.setAttribute('src', maps[i]);
}
previousBtn.addEventListener('click', prev);
nextBtn.addEventListener('click', next);
viewButtons(i);

Number elements by class and get placement of specific div

I have a list like this:
<div class="list"></div>
<div class="list"></div>
<div class="list on"></div>
<div class="list"></div>
... etc
I get the total like this:
var count = $('.list').length; // 4 in this case
But I want to write a function that selects the next div with the down arrow, and I can't figure out how to get the next div, select it, and deselect the current div. This is what I have now, but it doesn't work:
var visible = $('.list:visible');
var on = $('.list.on:visible');
if (e.keyCode == 40) { // down key
if(on.length == 0) {
visible.first().addClass("on"); // this works
}
else if( // div placement // < count) {
var next = on.next(); // this part doesn't work
on.removeClass("on"); // :(
next.addClass("on"); // :(
}
else { // if its the last div
// do nothing
}
}
You can do:
if (e.keyCode == 40) { // down key
if(on.length == 0) {
visible.first().addClass("on");
}
else if(on.length && on.next("div.list").length > 0) {
var next = on.next("div.list");
on.removeClass("on");
next.addClass("on");
}
else { // if its the last div
// do nothing
}
}
A quick demo: http://jsfiddle.net/GL7tA/
You can work with .index() to see which item is currently "selected":
var $items = $('.list:visible'),
$selectedItem = $items.filter('.on'),
selectedIndex = $items.index($selectedItem);
if (selectedIndex == -1) {
$items.eq(0).addClass('on');
} else if (selectedIndex + 1 < $items.length) {
$selectedItem.next().andSelf().toggleClass('on');
}
You have a syntax error in the else if ( part, where you say it doesn't work.
It doesn't work because the else if is expecting a condition to evaluate but you are not even closing the parenthesis of that if condition, neither have you openned the brackets for its block.

How to update variable in javascript/jquery after click?

I am trying to write some code that only allows buttons with the .add class function on click if there are fewer than 3 immediate children divs within the #hold placeholder div. theDiv_append is a seperate function that adds a div block within #hold div placeholder. I am trying to update the countingdivs variable to then count and update the number of divs within #hold, but it's not working. Currently, the add button does not get disabled.
var countingdivs = '1';
if(countingdivs <= '2') {
$(document).on('click',".add",theDiv_append);
countingdivs = $("#hold > div").length;
};
Any help would be great. Thanks!
Move the condition to the click handler:
$(document).on('click', '.add', theDiv_append);
function theDiv_append() {
var $hold = $('#hold'),
len = $hold.children('div').length;
if (len <= 2) {
// ...
}
}
Try:
var countingdivs = 1;
if(countingdivs <= 2) {
$(document).on('click',".add", theDiv_append);
countingdivs = $("#hold > div").length;
} else {
$(document).off('click',".add", theDiv_append);
}
Maybe try assigning countingdivs with the current length of #hold > div
var countingdivs = $("#hold > div").length;

Is there a simpler way to check what N child of some parent an element is?

An example:
parent
0 child-a
1 child-b
sub-child
2 child-c
sub-child
sub-child
This works currently in my fiddle, just wondering if there is a cleaner way to go about it. But If a child-(a|b|c) is clicked, the corresponding index is returned (much like jQuery's .index()... but if you click a child's sub-child, you get that same corresponding index.
Code:
$j = jQuery.noConflict();
$j(function() {
// when clicking , show which top level parent element it's in
var element_to_stop_at = $j(".the_top_level_container");
$j(document).click(function(event) {
var target = $j(event.target);
var index_of_top_level_element = 0;
var found_target = false;
var current = target;
while ((current.length > 0) && current.attr("class") != element_to_stop_at.attr("class")) {
current = current.parent();
}
var children = current.children();
current = children.first();
while (current.length > 0 && index_of_top_level_element < children.length) {
if (current.has(target).length > 0 || current[0] == target[0]) {
found_target = true;
break;
}
current = current.next();
index_of_top_level_element += 1;
}
if (false == found_target) {
index_of_top_level_element = -1;
}
console.log("is the " + index_of_top_level_element + " th/nd/rd element in the containing element");
});
});​
Is there a simplar / quicker way to do this?
Basically, I would like the functionality of .index(), but if I click a child of one of .the_top_level_container's children, it should return the same index.
fiddle http://jsfiddle.net/DerNalia/4LSzt/
• click any of the 3 sections boxed/bordered in
• The console will give the index of the the_top_level_container's children that the clicked element is a part of
Starting at the e.target, use .closest() to get to the child of the top level container, then use .index().
// child selector-------------------------v
var idx = $j(event.target).closest('.the_top_level_container > *')
.index();
DEMO: http://jsfiddle.net/4LSzt/22/
I don't know if it's what you need but check the index() method:
http://api.jquery.com/index/

How can I find all text nodes between two element nodes with JavaScript/jQuery?

Given the following HTML-Fragment:
<div>
<p>
abc <span id="x">[</span> def <br /> ghi
</p>
<p>
<strong> jkl <span id="y">]</span> mno </strong>
</p>
</div>
I need an algorithm to fetch all nodes of type Text between #x and #y with Javascript. Or is there a JQuery function that does exactly that?
The resulting Text nodes (whitespace nodes ignored) for the example above would then be:
['def', 'ghi', 'jkl']
The following works in all major browsers using DOM methods and no library. It also ignores whitespace text nodes as mentioned in the question.
Obligatory jsfiddle: http://jsfiddle.net/timdown/a2Fm6/
function getTextNodesBetween(rootNode, startNode, endNode) {
var pastStartNode = false, reachedEndNode = false, textNodes = [];
function getTextNodes(node) {
if (node == startNode) {
pastStartNode = true;
} else if (node == endNode) {
reachedEndNode = true;
} else if (node.nodeType == 3) {
if (pastStartNode && !reachedEndNode && !/^\s*$/.test(node.nodeValue)) {
textNodes.push(node);
}
} else {
for (var i = 0, len = node.childNodes.length; !reachedEndNode && i < len; ++i) {
getTextNodes(node.childNodes[i]);
}
}
}
getTextNodes(rootNode);
return textNodes;
}
var x = document.getElementById("x"),
y = document.getElementById("y");
var textNodes = getTextNodesBetween(document.body, x, y);
console.log(textNodes);
The following example uses jQuery to find any two elements that are next to each other and may or may not have text nodes between them. This foreach loop will check the resulted elements to find any text nodes and add them to the list.
function getTextNodes() {
var list = [];
$(document.body).find("*+*").toArray().forEach(function (el) {
var prev = el.previousSibling;
while (prev != null && prev.nodeType == 3) {
list.push(prev);
prev = prev.previousSibling;
}
});
return list;
}

Categories