"Cut and Paste" - moving nodes in the DOM with Javascript - javascript

I have html code that looks roughly like this:
<div id="id1">
<div id="id2">
<p>some html</p>
<span>maybe some more</span>
</div>
<div id="id3">
<p>different text here</p>
<input type="text">
<span>maybe even a form item</span>
</div>
</div>
Obviously there's more to it than that, but that's the basic idea. What I need to do is switch the location of #id2 and #id3, so the result is:
<div id="id1">
<div id="id3">...</div>
<div id="id2">...</div>
</div>
Does anyone know of a function (I'm sure I'm not the first person to require this functionality) that can read and write the two nodes (and all their children) so as to swap their location in the DOM?

In this case, document.getElementById('id1').appendChild(document.getElementById('id2')); should do the trick.
More generally you can use insertBefore().

This function takes any node that is passed into it and wraps it with the tag given. In the example code snippet I wrapped a span tag with a section tag.
function wrap(node, tag) {
node.parentNode.insertBefore(document.createElement(tag), node);
node.previousElementSibling.appendChild(node);
}
function wrap(node, tag) {
node.parentNode.insertBefore(document.createElement(tag), node);
node.previousElementSibling.appendChild(node);
}
let toWrap = document.querySelector("#hi");
wrap(toWrap, "section");
console.log(document.querySelector("section > #hi"), " section wrapped element");
<span id="hi">hello there!</span>

You can use
insertAdjacentElement instead of appendChild to have more control about the position of element with respect to a target element.
Syntax: targetElement.insertAdjacentElement(position, element).
It has four position codes as:
'beforebegin': Before the targetElement itself.
'afterbegin': Just inside the targetElement, before its first child.
'beforeend': Just inside the targetElement, after its last child.
'afterend': After the targetElement itself.
it appears as:
//beforebegin
<p>
//afterbegin
foo
//beforeend
</p>
//afterend
In your case, you can write the code as:
document.getElementById('id2').insertAdjacentElement('beforebegin', document.getElementById('id3'));
Note that this way, you don't need reference the parent (container) element!
Also consider You have more elements than id2, id3, eg: id4, id5, id6. Now, if you want to reposition for example id5 after id2, its as simple as:
function changePosition() {
document.getElementById('id2').insertAdjacentElement('afterend', document.getElementById('id5'));
}
<div id='container'>
<div id='id1'>id1</div>
<div id='id2'><u>id2</u></div>
<div id='id3'>id3</div>
<div id='id4'>id4</div>
<div id='id5'><b>id5</b></div>
<div id='id6'>id6</div>
</div>
<p><input type='button' onclick="changePosition()" value="change position"></p>

In my opinion is worth adding that if you need just a visual change (the DOM will stay the same but I will change in the UI) you can use the CSS order property.
It is probably more efficient that working on the DOM like the other answers, althought again doesn't really change the DOM structure so of course is not a real answer to this question.
Example:
document.addEventListener("DOMContentLoaded", function () {
const btnEl = document.getElementById('btn-swap');
const elToSwap = document.getElementById('id2');
btnEl.addEventListener('click', e => {
elToSwap.classList.toggle("first");
});
});
.container {
display: flex;
flex-direction: column;
}
.first {
order: -1;
}
<div class="container">
<div id="id1">first DIV</div>
<div id="id2">second DIV</div>
</div>
<button id="btn-swap">swap divs</button>

Short
I just add button (at the bottom) and js to your html
id3.after(id2);
function swap() {
id3.after(id2);
}
<div id="id1">
<div id="id2">
<p>some html</p>
<span>maybe some more</span>
</div>
<div id="id3">
<p>different text here</p>
<input type="text">
<span>maybe even a form item</span>
</div>
</div>
<button onclick="swap()">swap</button>

Related

Javascript:: Select sibling(s) object(s) with querySelector

Title is pretty much self explanatory...
Here is my code :
<div id=player>
<div class="button hand">►</div>
<div class=time>00:00/00:00</div>
<div class="timeline hand"><span class="now hand"></span></div>
</div>
I want to be able to get the <span class="now hand"></span> which is in between <div class="timeline hand"></div> via querySelector
var now=document.querySelector('#player>_____________.now.hand');
I'm also thinking if there is more convenient way to pick object from the relative id by children(s) or sibling(s) number instead of using id or class name.
You use a standard descendant selector (a space between #player and .now.hand):
var text = document.querySelector("#player .now.hand").innerHTML;
var p = document.createElement('p');
p.innerHTML = "Text is '" + text + "'";
document.body.appendChild(p);
<div id=player>
<div class="button hand">►</div>
<div class=time>00:00/00:00</div>
<div class="timeline hand"><span class="now hand">text in now hand</span></div>
</div>
I'm also thinking if there is more convenient way to pick object from the relative id by children(s) or sibling(s) number instead of using id or class name.
If this is in an event handler (or anywhere else you start out with a reference to some element), yes, you can use parentNode to find a parent (or repeatedly to find an ancestor), you can use querySelector on an element to only look within it, etc.
So for example:
document.body.addEventListener("click", function(e) {
var targetClass = e.target.className;
if (/\bbutton\b/.test(targetClass) && /\bhand\b/.test(targetClass)) {
alert(e.target.parentNode.querySelector(".now.hand").innerHTML);
}
}, false);
.button.hand {
color: blue;
font-weight: bold;
}
.now.hand {
color: green;
}
<div>
<div class="button hand">Click me</div>
<div class=time>00:00/00:00</div>
<div class="timeline hand"><span class="now hand">First hand</span></div>
</div>
<div>
<div class="button hand">Click me</div>
<div class=time>00:00/00:00</div>
<div class="timeline hand"><span class="now hand">Second hand</span></div>
</div>
<div>
<div class="button hand">Click me</div>
<div class=time>00:00/00:00</div>
<div class="timeline hand"><span class="now hand">Third hand</span></div>
</div>
Your span does not have any siblings. The easiest way to select it here would be document.querySelector('.now.hand') (Did you mean to apply two classes to the span? If not, connect them w/ a hyphen to use one class)
If you want to specify the span that is a descendant of the player, this would work:
js
document.querySelector('#player span.now.hand')
Learning to use CSS selectors will help here.

Toggle individual divs in a loop

In rendering out data within HTML, which prints out a div down the page, for every row found in the database, I'm trying to find a way to allow each button that sits in each div to toggle the individual example when clicked (with a default of display:none upon loading the page) - something such as:
function toggle_div(id) {
var divelement = document.getElementById(id);
if(divelement.style.display == 'none')
divelement.style.display = 'block';
else
divelement.style.display = 'none';
}
An example of the final markup :
<div>
<div class="wordtitle">Word</div>
<div class="numbers">1</div>
<div class="definition">Definition</div>
<button class="button" id="show_example" onClick="toggle_div('example')">Show example</button>
<div class="example" id="example">Example 1</div>
</div>
<div>
<div class="wordtitle">Word</div>
<div class="numbers">2</div>
<div class="definition">Definition</div>
<button class="button" id="show_example" onClick="toggle_div('example')">Show example</button>
<div class="example" id="example">Example 2</div>
</div>
getElementById() only toggles the first div's example, and getElementsByClass() hasn't seemed to work so far - not too sure how to do this - any ideas much appreciated!
First rule, do not insert multiple elements with the same ID. IDs are meant to be unique.
What you need is to toggle the example near the button you clicked, and not any (or all) .example to be showed / hidden. To achieve this, considering you used the [jquery] tag in your question, you can either use a selector to get the nearest .example of your button, or use jQuery's built-in functions to get it (.siblings()).
I would personally put the onclick out of your markup, and bind this custom function in your javascript.
Another important thing : if javascript is disabled client-side, the user won't ever see your example, as they are hidden by default in CSS. One fix would be to hide it initially with JS (see the snippet for this).
Here's a demonstration of what I mean :
$('.example-trigger').click(function() {
//Use the current button which triggered the event
$(this)
//Find the sibling you want to toggle, of a specified class
.siblings('.example-label')
//Toggle (hide or show) accordingly to the previous display status of the element
.toggle();
});
//Encouraged : hide examples only if Javascript is enabled
//$('.example-label').hide();
.example-label {
display: none;
/* Discouraged : if javascript is disabled, user won't see a thing */
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div>
<button class="example-trigger">Toggle example 1</button>
<span class="example-label">Example 1</span>
</div>
<div>
<button class="example-trigger">Toggle example 2</button>
<span class="example-label">Example 2</span>
</div>
<div>
<button class="example-trigger">Toggle example 3</button>
<span class="example-label">Example 3</span>
</div>
As #Sean stated, you need the ID to be unique since that's the way you are getting your elements.
$words .= '<div class="wordtitle">' . $word .'</div>
<div class="numbers">' . $i . '</div>
<div class="definition">' . $definition . '</div>
<button class="button" id="show_example" onClick="toggle_div(\'example\''.$i.')">
show example</button>
<div class="example" id="example'.$i.'">' . $example . '</div>
<br/><br/>';
$i++;
#show_example will also be repeating so you will probably want to change that to a class.
Another answer, only because I had the answer ready and was called away before I could post it. So here it is.
Notice that for repeating elements, classes are used instead of IDs. They work just as well, and (as everyone else has already said), IDs must be unique.
jsFiddle demo
HTML:
<div class="def">
<div class="wordtitle">Aardvark</div>
<div class="numbers">1</div>
<div class="definition">Anteater</div>
<button class="button show_example">show example</button>
<div class="example" id="example">The aardvark pushed its lenghty snout into the anthill and used its long, sticky tongue to extract a few ants.</div>
</div>
<div class="def">
<div class="wordtitle">Anecdote</div>
<div class="numbers">2</div>
<div class="definition">Amusing Story</div>
<button class="button show_example">show example</button>
<div class="example" id="example">The man told an anecdote that left everyone laughing.</div>
</div>
jQuery:
var $this;
$('.show_example').click(function() {
$this = $(this);
if ( $this.hasClass('revealed') ){
$('.example').slideUp();
$('.show_example').removeClass('revealed');
}else{
$('.example').slideUp();
$('.show_example').removeClass('revealed');
$this.parent().find('.example').slideDown();
$this.addClass('revealed');
}
});

setAttribute use for Javascript functions

I have 3 nested divs..
<div onclick="highlight(this)">1
<div onclick="highlight(this)">2
<div onclick="highlight(this)">3
</div>
</div>
</div>
To stop event-bubbling, I want add a syntax to the divs - stopPropagation().
I've tried (for first div only here)
document.querySelectorAll("div")[0].setAttribute("onclick", "event.stopPropagation()");
But it's not working. What is the solution/alternative to this..??
I want the divs to be like..
<div onclick="highlight(this) event.stopPropagation()">1
As you need to stop propagation of event, it seems to make sense that the corresponding action is attached to event itself. Here's one possible way of using it:
HTML
<div id="outer" onclick="highlight(event, this)">
<div id="middle" onclick="highlight(event, this)">
<div id="inner" onclick="highlight(event, this)">
</div>
</div>
</div>
JS
function highlight(event, target) {
event.stopPropagation();
console.log( target.id + ' is clicked' );
}
Demo.

Javascript click specific to ID

I have a div setup like so:
<div class="content">
<button class="show-comments" id="content1"></button>
</div>
<div class="comments-wrapper" id="comment1"></div>
<div class="content">
<button class="show-comments" id="content2"></button>
</div>
<div class="comments-wrapper" id="comment2"></div>
I have the following code:
$('.show-comments').click(function(e){
e.preventDefault();
$('.comments-wrapper').slideToggle('slow');
});
As you would assume, the code works but on a class basis. I'd like for it to open up only the .comments-wrapper of its associated id (i.e. open slideToggle comments2 if content 2 button is clicked and so on and so on).
How would I do this?
$('.show-comments').click(function(e){
e.preventDefault();
$(this).closest(".content").next('.comments-wrapper').slideToggle('slow');
});
Note that this is dependent on the .content element being immediately followed by the .comments-wrapper.
If you have access to modify the html itself, I would suggest adding a wrapper element and then doing the following to avoid the reliance on the exact order of elements:
<div class="wrapper">
<div class="content">
<button class="show-comments" id="content1"></button>
</div>
<div class="comments-wrapper" id="comment1"></div>
</div>
<div class="wrapper">
<div class="content">
<button class="show-comments" id="content2"></button>
</div>
<div class="comments-wrapper" id="comment2"></div>
</div>
$(this).closest(".wrapper").find('.comments-wrapper').slideToggle('slow');
This way, if you add an element between the .content and the .comments-wrapper it does not break the code.
You can do this:
$(this).parent("div").next('.comments-wrapper').slideToggle('slow');
This will find the related div of class .comments-wrapper and slide toggle.
And a fiddle: http://jsfiddle.net/xCJQB/
$('.show-comments').click(function (e) {
e.preventDefault();
var num = this.id.match(/\d+$/)[0];
$("#comment" + num).slideToggle('slow');
});
Demo ---> http://jsfiddle.net/7pkyk/1/
Use this context
$(this).closest('.comments').next('.comments-wrapper').slideToggle('slow');
If it is not the immediate element then you might try this as well
$(this).closest('.comments')
.nextAll('.comments-wrapper').first().slideToggle('slow');
you can add a common class to associate a button with a div.
html:
<div class="content">
<button class="show-comments group1" id="content1"></button>
</div>
<div class="comments-wrapper group1" id="comment1">1</div>
<div class="content">
<button class="show-comments group2" id="content2"></button>
</div>
<div class="comments-wrapper group2" id="comment2">2</div>
javascript:
$('.show-comments').click(function(e){
var associate = $(this).attr('class').match(/group\d+/).pop();
var selector = '.comments-wrapper.' + associate;
e.preventDefault();
$(selector).slideToggle('slow');
});
http://jsfiddle.net/uMNfJ/

How should I edit strings across many instances of the same html?

I've got this code below, with different data, repeated over 10 times on the page I am working on:
HTML:
<div class="kpaGraph">
<p>Target: 43%</p>
<div class="progress">
<div class="bar"></div>
</div>
</div>
<div class="kpaBottom">
<div class="strong">
<p>311</p>
</div>
<div class="weak">
<p>number of teachers trained</p>
</div>
</div>
I want to alter the number in div.strong p (311) based on the number in div.kpaGraph p (43%) in the same manner across all instances of this code with Javascript/ jQuery. What is the cleanest way to do this? Should I select all $('div.kpaGraph p') and then use each() Or should I create a function and run it on all of them?
Thanks!
You can use the following to find the proper element in conjuntion with an .each() on $('div.kpaGraph p'):
$(this).parent().next('div.kpaBottom').find('div.strong p')
For example, using the following will take the value in the kpaGraph p node and append it to the p node in the following kpaBottom node:
$('div.kpaGraph p').each(function () {
$(this).parent().next('div.kpaBottom').find('div.strong p').html('foo');
});
jsFiddle example
There are a few ways.
You can use "next".
$('.kpaGraph').each(function(){
var $kpaStrong = $(this).next('.kpaBottom .strong p');//this is the elm that has 311
});
Or you have to somehow create a relation between them so you know they go together, like a common parent.
<div class="kpaWr">
<div class="kpaGraph">
<p>Target: 43%</p>
<div class="progress">
<div class="bar"></div>
</div>
</div>
<div class="kpaBottom">
<div class="strong">
<p>311</p>
</div>
<div class="weak">
<p>number of teachers trained</p>
</div>
</div>
</div>
Then with jQuery you can select it like so:
$('.kpaGraph').each(function(){
var $kpaStrong = $(this).closest('.kpaWr').find('.kpaBottom .strong p');//this is the elm that has 311
});
Something like this might be pretty clean too:
$("div.strong p").text(function(index, text){
return $(this).closest("div.kpaBottom").prev("div.kpaGraph").find("p").text();
});
That would change the text to Target: 43% in your example.

Categories

Resources