Is there the HTML which is being ignored by CSS? - javascript

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.

Related

Jquery: Give styling (padding) of the parent class based on the condition that the first child matches with the class I want

Parent Class: cmp-container
There are two child classes -
productmodelfootnotes
footnotes
According to the sequence of the child I want to give padding as follows -
If "productmodelfootnotes" child class comes first -> Apply padding=40px to the parent class (cmp-container)
If "footnotes" child class comes first -> Apply padding=64px to the parent class (cmp-container)
First of all, initialize your parent element
let parent = document.querySelector('.cmp-container')
Then add style, based on condition
let firstChild = parent.children[0];
if(firstChild.classlist.contains('productmodelfootnotes'){
parent.style.padding = "40px";
} else if(firstChild.classlist.contains('footnotes')) {
parent.style.padding = "64px";
}
Consider the following.
$(function() {
$(".cmp-container").each(function(i, el) {
if ($("div:eq(0)", el).hasClass("productmodelfootnotes")) {
$(el).addClass("shortpad");
} else if ($("div:eq(0)", el).hasClass("footnotes")) {
$(el).addClass("longpad");
}
});
});
.cmp-container {
border: 1px solid blue;
margin-bottom: -1px;
}
.cmp-container .footnotes {
border: 1px solid red;
}
.cmp-container .productmodelfootnotes {
border: 1px solid green;
}
.shortpad {
padding: 40px;
}
.longpad {
padding: 64px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="cmp-container">
<div class="productmodelfootnotes">
Product Note 1
</div>
<div class="footnotes">
Foot Note 1
</div>
</div>
<div class="cmp-container">
<div class="footnotes">
Foot Note 2
</div>
</div>
<div class="cmp-container">
<div class="productmodelfootnotes">
Product Note 3
</div>
<div class="footnotes">
Foot Note 3
</div>
</div>
<div class="cmp-container">
<div class="footnotes">
Foot Note 4
</div>
<div class="productmodelfootnotes">
Product Note 4
</div>
</div>
Using .each(), we can iterate over the DIV elements and check the order of the children. If they meet the specific condition, we can add a Class or apply a Style to that element.
See More: https://api.jquery.com/each/
I assign i as the Index and el as the Element. I then use a Find selector, $("div:eq(0)", el) which is the same as $(el).find("div").eq(0) where 0 represents the first item in the list or elements that is Found.
Using .hasClass() we can check if it is true or false, and perform a specific action based on the condition.
See More: https://api.jquery.com/hasClass/
I used an if / else if statement to check two conditions. This is in case another element may appear first that should not have any padding.

How to apply a class to elements based on their z-Index value? (using javascript)

I'm trying to change the opacity of all elements on my document, except for the one I clicked (which has the highest z-Index).
Here is the code I'm using, am I accessing the z-Index's wrongly? When run, the opacity of the whole page changes (including those with a z-Index higher than 6).
allElements = document.getElementsByTagName("*")
for (let i = 0; i < allElements.length; i++) {
if (allElements[i].style.zIndex < 6)
allElements[i].style.opacity='0.7'
}
I would suggest a cleaner and more robust approach based on classes.
Basically use event listeners and toggle classes on your body and your highlightable items. The rest is just CSS as you would imagine.
resetAllHighlights = () => [...document.querySelectorAll('.item')].map(e => e.classList.remove('highlighted'));
toggleHighlightMode = (highlightMode) => {
if (highlightMode) document.querySelector('body').classList.add("highlight-enabled");
else document.querySelector('body').classList.remove("highlight-enabled");
return highlightMode = !highlightMode;
};
[...document.querySelectorAll('.item')].map(e => e.addEventListener('click', (e) => {
resetAllHighlights()
toggleHighlightMode(true)
e.currentTarget.classList.add('highlighted');
}));
.item {
height:100px;
width: 100px;
background-color: red;
margin: 5px;
opacity: 1;
}
body {
display: flex;
}
body.highlight-enabled .item:not(.highlighted) {
opacity: 0.5;
}
<body class="">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</body>
When you access an element's .style property, you will only have access to the styles that were established on the element via the HTML style attribute. If the styling was done via the class attribute or set with JavaScript, the .style property won't be able to access it. In those cases, you must use .getComputedStyle(), which doesn't care how or where the styling was set.
And speaking of how to set styles, it's always better to set styles using pre-made CSS classes and then just add or remove the class(es) as needed instead of setting individual styles. Using classes reduces duplication of code, is easier to manage, and scales better. You can easily access and add, remove or toggle classes with the .classList API.
Also (FYI), don't use .getElementsByTagName() as this is a legacy method that returns a "live node list", which can hurt performance. Instead, use .querySelectorAll()
So, here's an example similar to what you are doing:
let divs = document.querySelectorAll("div.opaque");
// Just set up one event handler at a common ancestor of
// all the elements that may trigger the event
document.addEventListener("click", function(event){
// Check to see if the event was triggered by
// an element you care to handle
if(event.target.classList.contains("opaque")){
// Loop over all the necessary elements
divs.forEach(function(div){
// Check the z-index using .getComputedStyle()
if(getComputedStyle(div).zIndex < 6){
// Instead of modifying a style directly,
// just add a pre-made class
div.classList.add("lightOpaque");
}
});
}
});
body { background-color: red; }
div { height:35px; border:1px dotted grey; position:relative; z-index:5; background-color:skyblue; }
.lightOpaque { opacity: .7; }
.special { position:relative; z-index:7; background-color:aliceblue; }
<div class="opaque">
</div>
<div class="opaque">
</div>
<div class="opaque special">
</div>
<div class="opaque">
</div>
<div class="opaque">
</div>
<div class="opaque">
</div>

How to use JavaScript to Alter CSS for Multiple Elements

I am trying to use JavaScript to change the background color of an element after being selected, and also to make sure that only one element at a time has the particular background color. Once the user selects on a different element I would like the previous element that was selected to be replaced by a different background color. Currently I am only able to toggle individual elements by selecting on EACH element. I need to be able to select on an element and apply the new background color, then have JavaScript change the background color of the previously active element to a different color (one less click).
What I am trying to do is very similar to modern navbars or list items where only one element at a time is “active” and has a background color that is different than the other elements in the same div, row, etc.
Notes about my work I am utilizing bootstrap and have no desire to use jQuery for this particular project.
CSS:
<!DOCTYPE html>
<html lang="en">
<head>
<style>
h4 {
border: 1px solid black;
border-radius: 8px;
padding: 10px 2px 10px 2px;
margin: 20px 20px 0px 20px;
background-color: #F0F0F0;
border-color: #F8F8F8;
color: #505050;
cursor: pointer;
}
.active {
background-color: #99E6FF;
}
</style>
</head>
</html>
HTML:
<div id="pTwoRowOne">
<div class="row">
<div class="col-md-4 row row-centered">
<h4 id="techBio" class="test">Biology</h4>
</div>
<div class="col-md-4 row row-centered">
<h4 id="techCart" class="test">Cartography</h4>
</div>
<div class="col-md-4 row row-centered">
<h4 id="techChem" class="test">Chemistry</h4>
</div>
</div>
</div>
JavaScript:
document.getElementById("techBio").onclick=function() {
document.getElementById("techBio").classList.toggle('active');
}
document.getElementById("techCart").onclick=function() {
document.getElementById("techCart").classList.toggle('active');
}
document.getElementById("techChem").onclick=function() {
document.getElementById("techChem").classList.toggle('active');
}
An example can be seen here: http://jsbin.com/fugogarove/1/edit?html,css,js,output
If clarification is needed let me know.
Yup, pretty straightforward.
Assumptions
You're not trying to support IE8, since you're using classList
You're okay with housing your elements as variables as opposed to repeatedly querying the DOM.
Example
JSBin
Code
I rewrote your JavaScript to make it a little bit cleaner and to DRY it up a bit:
var techs = [].slice.call(document.querySelectorAll('#pTwoRowOne h4'));
function set_active(event) {
techs.forEach(function(tech){
if (event.target == tech) { return; }
tech.classList.remove('active');
});
event.target.classList.toggle('active');
}
techs.forEach(function(item) {
item.addEventListener('click', set_active);
});
Some explanation
[].slice.call(document.querySelectorAll('#pTwoRowOne h4')); – We're using this to change the output from a NodeList to an Array. This allows us to use forEach later. querySelectorAll returns a NodeList that contains all elements matching the CSS selector. You can probably replace that with a better CSS selector depending on your environment.
addEventListener is a much nicer way than the iterative add via onclick += to bind an event listener. It's also the recommended way (as far as I know) in ECMA5 and later.
By setting the element queries as variables, you'll be able to keep the reference in memory instead of polling the DOM every time to alter elements. That'll make your JavaScript marginally faster, and it's again just a nicer, cleaner version of the code which it produces.
updates
I reworked the JS to make more sense.
Assuming you only ever have one active element, you can find it using document.querySelector() - if you can have multiples you can use document.querySelectorAll() and iterate through them.
Simple case:
function activate(event) {
var active=document.querySelector('.active');
// activate the clicked element (even if it was already active)
event.target.classList.add('active');
// deactivate the previously-active element (even if it was the clicked one => toggle)
if (active) active.classList.remove('active');
}
document.getElementById("techBio").addEventListener("click",activate);
document.getElementById("techCart").addEventListener("click",activate);
document.getElementById("techChem").addEventListener("click",activate);
h4 {
border: 1px solid black;
border-radius: 8px;
padding: 10px 2px 10px 2px;
margin: 20px 20px 0px 20px;
background-color: #F0F0F0;
border-color: #F8F8F8;
color: #505050;
cursor: pointer;
}
.active {
background-color: #99E6FF;
}
<div id="pTwoRowOne">
<div class="row">
<div class="col-md-4 row row-centered">
<h4 id="techBio" class="test">Biology</h4>
</div>
<div class="col-md-4 row row-centered">
<h4 id="techCart" class="test">Cartography</h4>
</div>
<div class="col-md-4 row row-centered">
<h4 id="techChem" class="test">Chemistry</h4>
</div>
</div>
</div>
Another similar yet simpler way to do it: jsBin ;)
var H4 = document.getElementsByClassName("test"), act;
[].forEach.call(H4, function(el){
el.addEventListener("click", function(){
if(act) act.classList.remove("active");
return (this.classList.toggle("active"), act=this);
});
});
You can do something like this:
[].slice.call(document.querySelectorAll(".test")).forEach(function(element) {
element.addEventListener('click', function(event) {
if (activeElement = document.querySelector(".test.active")) {
activeElement.classList.remove("active");
};
event.target.classList.add('active');
});
});
Basically, first we remove the active class from the active element, then we add it to the target.
JSBin

Make HTML element inherit events from custom object

In the webpage I'm working I have a lot of small images to wich I want to assign the same set of events. Instead of adding them one by one, I thought it would be more elegant if I could make that type of element inherit these events.
What comes to my mind is something like :
function InheritEvents(){};
InheritEvents.prototype.onmouseover = function(){...action a..};
InheritEvents.prototype.onmouseout = function(){...action b..};
var temp = originalHTMLElement.constructor; //(in this case img)
originalHTMLElement.prototype = new InheritEvents();
originalHTMLElement.constructor = temp;
a) Am I not disturbing the originalHTMLElement ?
b) Is it possible to name the custom object property, for example
".onmouseover" like in the classic way:
originalHTMLElement.onmouseover = function()... ?
c) More conceptual: Is it possible to mix your custom objects with HTML
elemenst / DOM nodes ?
I would strongly recommend against this. It probably wouldn't work anyway, but messing with the prototypes of host objects is, in general, a bad idea.
I don't think there should really be a problem with iterating through the target elements and attaching events to them, but if you don't like that, you can use event delegation:
window.onload = function() {
document.getElementById("images").onclick = function(e) {
if (e.target && e.target.classList.contains("clickable")) {
e.stopPropagation();
console.log("I've been clicked!");
}
}
}
#images div {
width: 40px;
height: 40px;
float: left;
margin: 5px;
background-color: blue;
}
#images div.clickable {
background-color: red;
}
#images + * {
clear: both;
}
<div>
<div id="images">
<!-- Pretend that these DIVs are your images -->
<div></div>
<div class="clickable"></div>
<div></div>
<div></div>
<div class="clickable"></div>
</div>
<div>
Click one of the red images above
</div>
</div>
Of course, if you're using jQuery, the .on() method can handle both the "add an event handler to all members of a set" option and the event delegation option in a single line.

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