Cannot get last child of div - javascript

I am trying to get the last child of a <div> but I am failing. Here is the code that I use for it but it always returns undefined.
$('#output:last-child').attr('class');
What is wrong? How can I fix it?
This is supposed to be the last element:
<div class='hisout ${hisclass}'><p class="he">` + message + '</p></div>

Use the child combinator selector:
$('#output > :last-child').attr('class');
Edit: Added example
#output > :last-child {
height: 20px;
width: 20px;
background: red;
border: 1px solid green;
}
<div id="output">
<div>....</div>
<div>....</div>
<div>....</div>
</div>

Your selector is retrieving the #output element that is the last child of its parent.
From the description it sounds like you want to find the last child within #output, as such you need to separate the selectors:
$('#output div:last-child').attr('class');

You can try with the children() and last() functions:
$('#output').children().last().attr('class')
To specifically filter a type of child, you can specify the type in the children() call:
...children('div')...
Using a selector may be faster, though.

Related

Is there the HTML which is being ignored by CSS?

Assume that in below HTML, the element with class ChildBar could be or could not be.
<div class="Parent">
<div class="ChildFoo"></div>
<div class="ChildBar"></div> <!-- Could not be -->
<div class="ChildBaz"></div>
</div>
Also, assume that ChildBaz must retire from ChildFoo by 4px but from ChildBar - by 6px. In CSS, it will be:
.ChildFoo + .ChildBaz {
margin-top: 4px;
}
.ChildBar + .ChildBaz {
margin-top: 6px;
}
Now, I want to mount by JavaScript the element ChildBar to correct position, herewith:
The changing of the markup around the ChildBar must not brake the JavaScript behaviour. It means the methods like Element.after() referes to sibling elements could not be used.
I need the mounting, not displaying from the hidden state.
The above styles must not be broken.
In the case of below markup, replaceWith solution would be easy.
<div class="Parent">
<div class="ChildFoo"></div>
<div id="ChildBarMountingPoint"></div>
<div class="ChildBaz"></div>
</div>
Hoewever, the element with ID ChildBarMountingPoint brakes the styles. Is there some magic element which is being ignored by CSS thus .ChildFoo + ChildBaz is being correctly appied? (If no, the solutions branch with replaceWith is a dead end and I must find the other solutions branch).
Is there a "magic" element that doesn't exist for the purpose of CSS selectors - No. But only elements are matched by selectors, and there are other node types. Maybe you could use one of those.
For example, one possibility is to use a comment. Assuming that you know that the two/three elements will be in a known parent whose class is .Parent you could do:
document.querySelector('#addChildBar').addEventListener('click', () => {
Array.from(document.querySelector('.Parent').childNodes).find(n => {
return n.nodeType === 8 && n.data === ' ChildBarMountingPoint '
}).replaceWith(Object.assign(document.createElement('div'), {
className: 'ChildBar'
}));
})
.ChildFoo + .ChildBaz {
margin-top: 4px;
color: red;
}
.ChildBar + .ChildBaz {
margin-top: 6px;
color: green;
}
<div class="Parent">
<div class="ChildFoo">foo</div>
<!-- ChildBarMountingPoint -->
<div class="ChildBaz">baz</div>
</div>
<hr>
<button type="button" id="addChildBar">Add ChildBar</button>
Comment nodes are node type 8.
If you don't know the parent, then you'll need to walk the DOM node by node to find the correct comment node.

Can't change .html()

I have a simple html (see here: http://plnkr.co/edit/nqfLNfYxV2B8AwsDAcNu?p=preview):
<section class="divs">
<div>div1
<div>div1.1.
<div>div1.1.1</div>
<div>div1.1.2</div>
</div>
<div>div1.2.</div>
</div>
<div>div2</div>
</section>
I want go through all section div:first-child elements and add some mark to see if it belongs to that selector. In order to implement this there is following code:
$(function(){
$('section div:first-child').each(function(index,element){
console.log('element.html before: '+$(element).html());
var elementHTML = '['+index+']: '+$(element).html();
$(element).html(elementHTML);
console.log('element.html after: '+$(element).html());
});
});
But it adds the [index] to the element's html only for the first selector.
Can anybody explain why?
UPD
Not quite the answer, but nevertheless, Pete gave me the understanding of the problem.
So in order to make it working there is necessary to change
$(element).html(elementHTML);
to
$('section div:first-child')
.eq(index).html(elementHTML);
It's not about efficiency ofcourse, it's about how to solve the problem in place. The real solution, no doubt, is to use .prepend() method.
Your problem is the first time instance of section div:first-child is the top level div (div1) so when you replace the html of this div, the divs 2 and 3 in the loop no longer exist in the dom and so they don't get updated as such
Instead of replacing the html, just prepend the text:
$('section div:first-child').each(function(index, element) {
$(element).prepend('[' + index + ']: ');
});
New plunker
$('section div:first-child')
https://developer.mozilla.org/en-US/docs/Web/CSS/%3Afirst-child
The :first-child CSS pseudo-class represents any element that is the
first child element of its parent.
If you want to select only the divs one level below section, you can try:
$('section > div')
Since you replace the HTML every time, the elements are replaced on the fly. To avoid that, don't replace the entire HTML, just $.prepend() to it. Try it:
$(function() {
$('section div:first-child').each(function(index, element) {
console.log('element.html before: ' + $(element).html());
var elementHTML = '[' + index + ']: ';
$(element).prepend(elementHTML); // <------------------------ HERE !
console.log('element.html after: ' + $(element).html());
});
});
body * { box-sizing: border-box; }
div { background-color: white; }
section div { border: solid 2px #888; margin: 4px; padding: 10px; }
section div:first-child { background-color: lightcyan; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<section class="divs">
<div>div1
<div>div1.1.
<div>div1.1.1</div>
<div>div1.1.2</div>
</div>
<div>div1.2.</div>
</div>
<div>div2</div>
</section>
When you set the html, the elements below it don't get updated, they get replaced
Your code works, it's just updating the elements that are no longer attached to the DOM.

How should i improve my jQuery click funciton?

So i'm learning some jQuery at the moment and got somewhat stuck with this .click function. I'm trying to "turn a light on and off", so to speak.
I am able to do so, but only once. Why is that, that my code only runs for one click event per item, and how should i improve it?
Link to my JSfiddle.
HTML
<div class="lightOn"></div>
<div class="lightOff"></div>
jQuery
$('.lightOn').click(function() {
$(this).removeClass('lightOn');
$(this).addClass('lightOff');
});
$('.lightOff').click(function() {
$(this).removeClass('lightOff');
$(this).addClass('lightOn');
});
CSS
.lightOn {
height: 90px;
width:90px;
background-color:yellow;
border-radius: 100%;
float:left;
margin:10px;
}
.lightOff {
height: 90px;
width:90px;
background-color:grey;
border-radius: 100%;
float:left;
margin:10px;
}
The issue is because you are removing the class you are selecting by, so for successive clicks the element no longer exists. Instead have a common class which remains, but add one to it to light up the object. Try this:
<div class="light"></div>
<div class="light"></div>
.light.on {
background-color:yellow;
}
.light {
height: 90px;
width:90px;
background-color:grey;
border-radius: 100%;
float:left;
margin:10px;
}
$('.light').click(function() {
$(this).toggleClass('on');
});
Example fiddle
This method has the benefit of being able to handle x number of .light elements wihtout having to amend the jQuery selector you use.
The problem is that you bind the functions to elements, not to selectors. That is to say, you bind a function that removes the class lightOn to the element that had that class originally. That function only ever removes the lightOn class and adds the lightOff class, even if that has already been done once.
There are two ways to fix this. One is with on and event delegation, which allows you to do something akin to binding to a selector. It attaches the handler to a parent element, and makes use of the fact that all ancestor elements are notified of events that originated on their descendents. So the function might be bound to document.body, but only elements that originated on an element matching the .lightOn selector will trigger the handler:
$(document.body).on('click', '.lightOn', function() {
$(this).removeClass('lightOn').addClass('lightOff');
}).on('click', '.lightOff', function() {
$(this).removeClass('lightOff').addClass('lightOn');
});
http://jsfiddle.net/lonesomeday/C6f7u/5/
Better, however, is to make use of jQuery's toggleClass function, which removes classes if the element currently has them and adds them if it doesn't.
$('.lightOn,.lightOff').click(function() {
$(this).toggleClass('lightOn lightOff');
});
http://jsfiddle.net/lonesomeday/C6f7u/2/
What about
$('.lightOn, .lightOff').click(function() {
$(this).toggleClass('lightOn lightOff');
});
Demo: Fiddle
You can try using toogleClass of jquery
http://api.jquery.com/toggleClass/
It's a good practice to attach your events to the parent element. In your case this is even mandatory, because you are changing the classes, which are used during the event binding. So, your HTML:
<div class="ligths">
<div class="lightOn"></div>
<div class="lightOff"></div>
</div>
JS:
$(".ligths").on("click", "div", function(e) {
var el = $(this);
if(el.hasClass("lightOn")) {
el.removeClass("lightOn").addClass("lightOff");
} else {
el.removeClass("lightOff").addClass("lightOn");
}
});
JSFiddle: http://jsfiddle.net/C6f7u/7/

javascript getElementById is null - Id passed by this

Maybe I'm not using 'this' right.
HTML:
<div style="margin: 4px; padding: 10px; border: solid 1px;" onmouseover="darken(this);">
JavaScript:
function darken(elt) {
"use strict";
document.getElementById(elt.id).style.backgroundColor = "#e8e8e8";
}
?
If you have a reference to the element, there's no need to lookup its ID then query the document for the element matching that ID. (There is no ID assigned to that element by the way).
this is a reference to the element. You could simply use it as:
function darken(elt) {
elt.style.backgroundColor = '#e8e8e8';
}
jsFiddle Demo
The first issue is that there is no id on that element so you cannot use document.getElementById to find it.
An advantage that you can use is that you already passed the element with this:
function darken(elt) {
"use strict";
elt.style.backgroundColor = "#e8e8e8";
}
You havn't provided any Id to your div element
<div id="myDiv" style="margin: 4px; padding: 10px; border: solid 1px;" onmouseover="darken(this);">
JS Fiddle Demo

How to move an element into another element

I would like to move one DIV element inside another. For example, I want to move this (including all children):
<div id="source">
...
</div>
into this:
<div id="destination">
...
</div>
so that I have this:
<div id="destination">
<div id="source">
...
</div>
</div>
You may want to use the appendTo function (which adds to the end of the element):
$("#source").appendTo("#destination");
Alternatively you could use the prependTo function (which adds to the beginning of the element):
$("#source").prependTo("#destination");
Example:
$("#appendTo").click(function() {
$("#moveMeIntoMain").appendTo($("#main"));
});
$("#prependTo").click(function() {
$("#moveMeIntoMain").prependTo($("#main"));
});
#main {
border: 2px solid blue;
min-height: 100px;
}
.moveMeIntoMain {
border: 1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="main">main</div>
<div id="moveMeIntoMain" class="moveMeIntoMain">move me to main</div>
<button id="appendTo">appendTo main</button>
<button id="prependTo">prependTo main</button>
My solution:
Move:
jQuery("#NodesToMove").detach().appendTo('#DestinationContainerNode')
copy:
jQuery("#NodesToMove").appendTo('#DestinationContainerNode')
Note the usage of .detach(). When copying, be careful that you are not duplicating IDs.
Use a vanilla JavaScript solution:
// Declare a fragment:
var fragment = document.createDocumentFragment();
// Append desired element to the fragment:
fragment.appendChild(document.getElementById('source'));
// Append fragment to desired element:
document.getElementById('destination').appendChild(fragment);
Check it out.
Try plain JavaScript: destination.appendChild(source);.
onclick = function(){ destination.appendChild(source) };
div {
margin: .1em;
}
#destination {
border: solid 1px red;
}
#source {
border: solid 1px gray;
}
<div id=destination>
###
</div>
<div id=source>
***
</div>
I just used:
$('#source').prependTo('#destination');
Which I grabbed from here.
If the div where you want to put your element has content inside, and you want the element to show after the main content:
$("#destination").append($("#source"));
If the div where you want to put your element has content inside, and you want to show the element before the main content:
$("#destination").prepend($("#source"));
If the div where you want to put your element is empty, or you want to replace it entirely:
$("#element").html('<div id="source">...</div>');
If you want to duplicate an element before any of the above:
$("#destination").append($("#source").clone());
// etc.
You can use:
To insert after,
jQuery("#source").insertAfter("#destination");
To insert inside another element,
jQuery("#source").appendTo("#destination");
You can use the following code to move the source to the destination:
jQuery("#source")
.detach()
.appendTo('#destination');
Try the working CodePen.
function move() {
jQuery("#source")
.detach()
.appendTo('#destination');
}
#source{
background-color: red;
color: #ffffff;
display: inline-block;
padding: 35px;
}
#destination{
background-color:blue;
color: #ffffff;
display: inline-block;
padding: 50px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="source">
I am source
</div>
<div id="destination">
I am destination
</div>
<button onclick="move();">Move</button>
If you want a quick demo and more details about how you move elements, try this link:
http://html-tuts.com/move-div-in-another-div-with-jquery
Here is a short example:
To move ABOVE an element:
$('.whatToMove').insertBefore('.whereToMove');
To move AFTER an element:
$('.whatToMove').insertAfter('.whereToMove');
To move inside an element, ABOVE ALL elements inside that container:
$('.whatToMove').prependTo('.whereToMove');
To move inside an element, AFTER ALL elements inside that container:
$('.whatToMove').appendTo('.whereToMove');
I need to move content from one container to another including all the event listeners. jQuery doesn't have a way to do it, but the standard DOM function appendChild does.
// Assuming only one .source and one .target
$('.source').on('click',function(){console.log('I am clicked');});
$('.target')[0].appendChild($('.source')[0]);
Using appendChild removes the .source* and places it into target including its event listeners: Node.appendChild() (MDN)
You may also try:
$("#destination").html($("#source"))
But this will completely overwrite anything you have in #destination.
You can use pure JavaScript, using appendChild() method...
The appendChild() method appends a node as the last child of a node.
Tip: If you want to create a new paragraph, with text, remember to
create the text as a Text node which you append to the paragraph, then
append the paragraph to the document.
You can also use this method to move an element from one element to
another.
Tip: Use the insertBefore() method to insert a new child node before a
specified, existing, child node.
So you can do that to do the job, this is what I created for you, using appendChild(), run and see how it works for your case:
function appendIt() {
var source = document.getElementById("source");
document.getElementById("destination").appendChild(source);
}
#source {
color: white;
background: green;
padding: 4px 8px;
}
#destination {
color: white;
background: red;
padding: 4px 8px;
}
button {
margin-top: 20px;
}
<div id="source">
<p>Source</p>
</div>
<div id="destination">
<p>Destination</p>
</div>
<button onclick="appendIt()">Move Element</button>
I noticed huge memory leak & performance difference between insertAfter & after or insertBefore & before .. If you have tons of DOM elements, or you need to use after() or before() inside a MouseMove event, the browser memory will probably increase and next operations will run really slow.
The solution I've just experienced is to use inserBefore instead before() and insertAfter instead after().
Dirty size improvement of Bekim Bacaj's answer:
div { border: 1px solid ; margin: 5px }
<div id="source" onclick="destination.appendChild(this)">click me</div>
<div id="destination" >...</div>
For the sake of completeness, there is another approach wrap() or wrapAll() mentioned in this article. So the OP's question could possibly be solved by this (that is, assuming the <div id="destination" /> does not yet exist, the following approach will create such a wrapper from scratch - the OP was not clear about whether the wrapper already exists or not):
$("#source").wrap('<div id="destination" />')
// or
$(".source").wrapAll('<div id="destination" />')
It sounds promising. However, when I was trying to do $("[id^=row]").wrapAll("<fieldset></fieldset>") on multiple nested structure like this:
<div id="row1">
<label>Name</label>
<input ...>
</div>
It correctly wraps those <div>...</div> and <input>...</input> BUT SOMEHOW LEAVES OUT the <label>...</label>. So I ended up use the explicit $("row1").append("#a_predefined_fieldset") instead. So, YMMV.
The .appendChild does precisely that - basically a cut& paste.
It moves the selected element and all of its child nodes.

Categories

Resources