event doesn't clone after deleting first element - javascript

I'm supposed to clone some elements with Jquery and it works well but when i delete the first element which i'm cloning the others with it, after that the new cloned elements don't have the events which supposed to have!
i tried .clone(true, true) method and it clone event but not after the deleting the first element.
var card = $(".newCard"); //class name of first element
$("#addBtn0").click(function() {
$(".row").append(card.clone(true, true)); //it works well but...
});
$("[class^=btnDelete]").click(function() {
$(this).closest(".newCard").remove(); //it works too but not after deleting first element and creating again
});
I don't know why this is happening, actually every element should have the events even after deleting the first element and recreate.

The problem is the click event is being bound to that first element, and as a result that binding is removed along with the element. When dealing with dynamic elements you should use event delegation by using the .on method on a static element. Such as the document itself.
EDIT: You won't notice any performance issues on a small document like this, but using the document as your event delegator can cause performance issues on larger documents. You can read more about event delegation performance here.
var card = $(".newCard");
$("#addBtn0").click(function() {
$(".row").append(card.clone(true, true));
});
$(document).on('click', '.btnDelete', function() {
$(this).closest(".newCard").remove();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="addBtn0">Add</button>
<div class="row">
<div class="newCard">Card <button class="btnDelete">Delete</button></div>
</div>

This happens because of
$("[class^=btnDelete]").click(function() {
the above line will target the existing (!!!) element and it's inner button.
Since you're cloning that existing element, you're also cloning the Event bound to it's button on-creation.
Once you delete that card (stored in variable), you're also destroying the Event bound to it.
To fix that issue use .on() with dynamic event delegation:
$(".row").on('click', '[class^=btnDelete]', function() {
var card = $(".newCard"); //class name of first element
$("#addBtn0").click(function() {
$(".row").append(card.clone(true, true));
});
$(".row").on('click', '[class^=btnDelete]', function() {
$(this).closest(".newCard").remove();
});
<div class="row">
<div class="newCard">CARD <button class="btnDelete">DELETE</button></div>
</div>
<button id="addBtn0">ADD</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
https://api.jquery.com/on/#direct-and-delegated-events
Other issues and solution
Other issues you have in your code are mainly naming stuff. [class^=btnDelete] is just waiting for you one day adding a style class to that poor button and see your JavaScript fail miserably. Also, why btnAdd0 what's the 0? Why .clone(true, true) at all?
Here's a better rewrite:
const $Cards = $('.Cards');
const $Cards_add = $('.Cards-btn--add');
const $Cards_item = (html) => {
const $self = $('<div/>', {
appendTo: $Cards,
class: `Cards-item`,
html: html || `New Card`,
});
$('<button/>', {
appendTo: $self,
class: `Cards-btn Cards-btn--delete`,
text: `Delete`,
on: {
click() {
$self.remove();
}
},
});
}
let card_id = 0;
$Cards_add.on('click', () => $Cards_item(`This is Card ${++card_id}`));
// Create some dummy cards
$Cards_item(`This is Card ${++card_id}`);
$Cards_item(`This is Card ${++card_id}`);
$Cards_item(`This is Card ${++card_id}`);
/**
* Cards component styles
*/
.Cards {} /* scss extend .row or rather define styles directly */
.Cards-item { padding: 5px; margin: 5px 0; background: #eee; }
.Cards-btn { }
.Cards-btn--add { color: blue; }
.Cards-btn--delete { color: red; }
<div class="Cards"></div>
<button class="Cards-btn Cards-btn--add">ADD</button>
<script src="//code.jquery.com/jquery-3.4.1.js"></script>

$(".row").append(card.clone(true, true));
You are still using the original result of $('.newCard') that does not include any new cards you've added.
$(".row").append($(this).parent().clone(true, true));
this works

Related

Change color of all elements in class on click JavaScript

I have an image (SVG) of a human body. I would like to use JavaScript so that when I click a particular area (say, the lower leg) then all of the elements with the class "lower-leg" (even if not clicked) have their color changed -- this makes it much easier for the user.
Here is the JavaScript I currently have:
function changeclassstyle() {
var c = document.getElementsByClassName("lower-leg");
for (var i=0; i<c.length; i++) {
c[i].style.fill = "red";
}
}
The problem with this code is that it is only generalized for "lower-leg". I may have over a dozen classes I would like this to work for and don't think it is efficient to write 12 functions with the only change being the class name. Is there a way to grab what class was selected and then input that in the function?
--
Additionally, I would love to figure out how, once that section of the body is selected, I can store the class name. I would, in the end, want to store the selection, along with other inputted information in a database. But, this may be for a future question unless someone can help!
Here's how I would do it (tested on a couple of div's).
What we're doing is passing the event object to the event handler (your changeclassstyle() function). It then uses the class of the clicked-on item (the event target's class) and changes everything else on that page with that same class name to use your new desired CSS style.
function changeclassstyle(e) {
// Get all items that have the same class as the item that was clicked
var limbs = document.getElementsByClassName(e.target.className); // for div's and the like
// var limbs = document.getElementsByClassName(e.target.className.baseVal); // turns out this is needed for SVG items
// "limbs" is an HTMLCollection, not an array, so functions like .foreach won't work; C-style for-loops or modern for/let/of loops are better
for (let item of limbs) {
item.style.backgroundColor = 'red';
// item.style.fill = 'red'; // This is probably what you need for your SVG items
}
// You could still use your C-style for loop if needed/wanted
/*
for (var i=0; i<limbs.length; i++) {
limbs[i].style.fill = "red";
}
*/
}
The onchange call looks like this (using my div as the example):
<div class="upper-arm" onclick="changeclassstyle(event)">
</div>
<div class="lower-leg" onclick="changeclassstyle(event)">
</div>
The whole example with simple div's.
<html>
<head><title>stuff</title></head>
<body>
<script type="text/javascript">
function changeclassstyle(e) {
// For debugging. You may want to expand 'e' here in your browser's debug tools if you're not seeing the values you need/want
console.log(e)
var limbs = document.getElementsByClassName(e.target.className.baseVal);
for (let item of limbs) {
item.style.backgroundColor = 'red';
}
}
</script>
<style type="text/css">
div {
height: 100px;
width: 100px;
background-color: 'white';
border: 1px solid black;
}
</style>
<div class="upper-arm" onclick="changeclassstyle(event)">
</div>
<div class="upper-arm" onclick="changeclassstyle(event)">
</div>
<div class="upper-arm" onclick="changeclassstyle(event)">
</div>
<div class="lower-leg" onclick="changeclassstyle(event)">
</div>
<div class="lower-leg" onclick="changeclassstyle(event)">
</div>
<div class="lower-leg" onclick="changeclassstyle(event)">
</div>
</body>
</html>
You can use parameters in function where you pass class and color like below
function changeStyle(cls,clr) {
let elems = document.getElementsByClassName(cls);
if(!elems) return;
for (let elem of elems) {
elem.style.color = clr;
}
}
As per the iteration of many classes like i said you can store classes in array and iterate each of them.
let classes = ['one','two','three','four'];
classes.forEach(function (cls) {
changeStyle(cls,"red");
});
You can play with fiddle here if you want to test/experiment: https://jsfiddle.net/thrL5uqw/8/
Note: Change style property as you wish, For now i have used color for demo
I'm a bit late to the party, but here's my take on the problem.
Like the others told you, you'll need to use an additional parameter to your function to specify the class you want to modify your elements (or try to figure out the class from the clicked element), therefore you should have something like that:
/**
* This function will handle the click event on one of the part of the SVG.
* #param {string} lClass This the class of the element to modify
*/
function handleClick(lClass) {
for (let e of document.getElementsByClassName(lClass)) {
// Here you can do all the changes you need on the SVG element.
e.style.fill = "red";
}
}
And when it comes to the event binding, you could do like the other suggested and add the onclick event binding propery on the HTML Element, or you could bind it in you JS with the addEventListener function (that way you don't have to repeat the onclick property on each of your SVG elements).
// For each element of all the listed class, bind the "click" event to the handleClick function
const listenClass = [/*List of your classes*/];
for (let l of listenClass) {
for (let e of document.getElementsByClassName(l)) {
e.addEventListener('click', handleClick.bind(this, l));
}
}
Demo: https://plnkr.co/edit/gay2yBaVi5QD868fsTa6?p=preview
I hope it helped.

Make all elements of a html class react on a click, which would modify the element itself

I am trying to write a tutorial for my students, in the form of a webpage with hidden "spoilers" that the student can unhide, presumably after thinking about the answer. So, long story short, the behavior I am looking for is:
in the beginning, the text appears with a lot of hidden words;
when a piece of text is clicked, it appears, and stays uncovered afterwards;
this should work with minimal overhead (not forcing me to install a complex framework) and on all my students' machines, even if the browser is outdated, even if jquery is not installed.
I searched for off the shelf solutions, but all those I checked were either too complicated or not doing exactly what I wanted. So I decided to do my own.
What I have so far is this:
<HTML>
<STYLE>
span.spoil {background-color: black;}
span.spoiled {background-color: white;}
</STYLE>
<HEAD>
<TITLE>SPOIL</TITLE>
<META http-equiv="Content-Type" content="text/html;charset=UTF-8">
<!--LINK rel="Stylesheet" type="text/css" href=".css"-->
</HEAD>
<BODY>
This is a text with <span class="spoil" onclick="showspoil(this)">spoil data</span>.
<br>
<span class="spoil" onclick="showspoil(this)">Unspoil me.</span>
<br>
<span class="spoil" onclick="showspoil(this)">And me.</span>
<script>
function showspoil(e) {
e.className="spoiled";
}
// var classname = document.getElementsByClassName("spoil");
// for (var i = 0; i < classname.length; i++) {
// classname[i].addEventListener('click', showspoil(WHATEXACTLY?), false);
// }
</script>
</BODY>
</HTML>
It does the job, except that I find it annoying to have to write explicitly the "onclick..." for each element. So I tried adding an event listener to each member of the class, by imitating similar resources found on the web: unfortunately, this part (the commented code above) does not work. In particular, I do not see which parameter I should pass to the function to transmit "the element itself".
Can anyone help? If I may play it lazy, I am more looking for an answer to this specific query than for pointers to a series of courses I should take: I admit it, I have not been doing html for a loooooong time, and I am sure I would need a lot of readings to be efficient again: simply, I do not have the time for the moment, and I do not really need it: I just need to solve this issue to set up a working solution.
Problem here is you are calling the method and assigning what it returns to be bound as the event listener
classname[i].addEventListener('click', showspoil(WHATEXACTLY?), false);
You can either use a closure or call the element directly.
classname[i].addEventListener('click', function () { showspoil(this); }, false);
or
classname[i].addEventListener('click', showspoil, false);
If you call it directly, you would need to change the function to
function showspoil(e) {
this.className="spoiled";
}
Another option would be to not bind click on every element, just use event delegation.
function showspoil(e) {
e.className="spoiled";
}
document.addEventListener("click", function (e) { //list for clcik on body
var clicked = e.target; //get what was clicked on
if (e.target.classList.contains("spoil")) { //see if it is an element with the class
e.target.classList.add("spoiled"); //if it is, add new class
}
});
.spoil { color: red }
.spoiled { color: green }
This is a text with <span class="spoil">spoil data</span>.
<br>
<span class="spoil">Unspoil me.</span>
<br>
<span class="spoil">And me.</span>
function unspoil() {
this.className = "spoiled"; // "this" is the clicked object
}
window.onload = function() {
var spoilers = document.querySelectorAll(".spoil"); // get all with class spoil
for (var i = 0; i < spoilers.length; i++) {
spoilers[i].onclick = unspoil;
}
}
span.spoil {
background-color: black;
}
span.spoiled {
background-color: white;
}
This is a text with <span class="spoil">spoil data</span>.
<br>
<span class="spoil">Unspoil me.</span>
<br>
<span class="spoil">And me.</span>
An additional approach could be to add the click-listener to the document and evaluate the event target:
document.addEventListener("click", function(e){
if (e.target.className == "spoil"){
e.target.className = "spoiled";
}
});
That way
you only need one event listener in the whole page
you can also append other elements dynamically with that class without the need for a new event handler
This should work, because the event's target is always the actual element being clicked. If you have sub-elements in your "spoil" items, you may need to traverse up the parent chain. But anyway I think this is the least resource-wasting way.
var spoilers = document.getElementsByClassName('spoil');
for(i=0;i<spoilers.length;i++){
spoilers[i].addEventListener('click',function(){
this.className = "spoiled";
});
}

How to add load event of dynamically added elements [duplicate]

How do you add an onload event to an element?
Can I use:
<div onload="oQuickReply.swap();" ></div>
for this?
No, you can't. The easiest way to make it work would be to put the function call directly after the element
Example:
...
<div id="somid">Some content</div>
<script type="text/javascript">
oQuickReply.swap('somid');
</script>
...
or - even better - just in front of </body>:
...
<script type="text/javascript">
oQuickReply.swap('somid');
</script>
</body>
...so it doesn't block the following content from loading.
You can trigger some js automatically on an IMG element using onerror, and no src.
<img src onerror='alert()'>
The onload event can only be used on the document(body) itself, frames, images, and scripts. In other words, it can be attached to only body and/or each external resource. The div is not an external resource and it's loaded as part of the body, so the onload event doesn't apply there.
onload event it only supports with few tags like listed below.
<body>, <frame>, <iframe>, <img>, <input type="image">, <link>, <script>, <style>
Here the reference for onload event
Try this! And never use trigger twice on div!
You can define function to call before the div tag.
$(function(){
$('div[onload]').trigger('onload');
});
DEMO: jsfiddle
I just want to add here that if any one want to call a function on load event of div & you don't want to use jQuery(due to conflict as in my case) then simply call a function after all the html code or any other code you have written including the function code and
simply call a function .
/* All Other Code*/
-----
------
/* ----At the end ---- */
<script type="text/javascript">
function_name();
</script>
OR
/* All Other Code*/
-----
------
/* ----At the end ---- */
<script type="text/javascript">
function my_func(){
function definition;
}
my_func();
</script>
I needed to have some initialization code run after a chunk of html (template instance) was inserted, and of course I didn't have access to the code that manipulates the template and modifies the DOM. The same idea holds for any partial modification of the DOM by insertion of an html element, usually a <div>.
Some time ago, I did a hack with the onload event of a nearly invisible <img> contained in a <div>, but discovered that a scoped, empty style will also do:
<div .... >
<style scoped="scoped" onload="dosomethingto(this.parentElement);" > </style>
.....
</div>
Update(Jul 15 2017) -
The <style> onload is not supported in last version of IE. Edge does support it, but some users see this as a different browser and stick with IE. The <img> element seems to work better across all browsers.
<div...>
<img onLoad="dosomthing(this.parentElement);" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" />
...
</div>
To minimize the visual impact and resource usage of the image, use an inline src that keeps it small and transparent.
One comment I feel I need to make about using a <script>is how much harder it is to determine which <div> the script is near, especially in templating where you can't have an identical id in each instance that the template generates. I thought the answer might be document.currentScript, but this is not universally supported. A <script> element cannot determine its own DOM location reliably; a reference to 'this' points to the main window, and is of no help.
I believe it is necessary to settle for using an <img> element, despite being goofy. This might be a hole in the DOM/javascript framework that could use plugging.
Avoid using any interval-based methods (as they are not performant and accurate) and use MutationObserver targeting a parent div of dynamically loaded div for better efficiency.
Update: Here's a handy function I wrote. Use it like this:
onElementLoaded("div.some_class").then(()=>{}).catch(()=>{});
/**
*
* Wait for an HTML element to be loaded like `div`, `span`, `img`, etc.
* ex: `onElementLoaded("div.some_class").then(()=>{}).catch(()=>{})`
* #param {*} elementToObserve wait for this element to load
* #param {*} parentStaticElement (optional) if parent element is not passed then `document` is used
* #return {*} Promise - return promise when `elementToObserve` is loaded
*/
function onElementLoaded(elementToObserve, parentStaticElement) {
const promise = new Promise((resolve, reject) => {
try {
if (document.querySelector(elementToObserve)) {
console.log(`element already present: ${elementToObserve}`);
resolve(true);
return;
}
const parentElement = parentStaticElement
? document.querySelector(parentStaticElement)
: document;
const observer = new MutationObserver((mutationList, obsrvr) => {
const divToCheck = document.querySelector(elementToObserve);
if (divToCheck) {
console.log(`element loaded: ${elementToObserve}`);
obsrvr.disconnect(); // stop observing
resolve(true);
}
});
// start observing for dynamic div
observer.observe(parentElement, {
childList: true,
subtree: true,
});
} catch (e) {
console.log(e);
reject(Error("some issue... promise rejected"));
}
});
return promise;
}
Implementation details:
HTML:
<div class="parent-static-div">
<div class="dynamic-loaded-div">
this div is loaded after DOM ready event
</div>
</div>
JS:
var observer = new MutationObserver(function (mutationList, obsrvr) {
var div_to_check = document.querySelector(".dynamic-loaded-div"); //get div by class
// var div_to_check = document.getElementById('div-id'); //get div by id
console.log("checking for div...");
if (div_to_check) {
console.log("div is loaded now"); // DO YOUR STUFF!
obsrvr.disconnect(); // stop observing
return;
}
});
var parentElement = document.querySelector("parent-static-div"); // use parent div which is already present in DOM to maximise efficiency
// var parentElement = document // if not sure about parent div then just use whole 'document'
// start observing for dynamic div
observer.observe(parentElement, {
// for properties details: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit
childList: true,
subtree: true,
});
we can use MutationObserver to solve the problem in efficient way adding a sample code below
<!DOCTYPE html>
<html>
<head>
<title></title>
<style>
#second{
position: absolute;
width: 100px;
height: 100px;
background-color: #a1a1a1;
}
</style>
</head>
<body>
<div id="first"></div>
<script>
var callthis = function(element){
element.setAttribute("tabIndex",0);
element.focus();
element.onkeydown = handler;
function handler(){
alert("called")
}
}
var observer = new WebKitMutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
for (var i = 0; i < mutation.addedNodes.length; i++)
if(mutation.addedNodes[i].id === "second"){
callthis(mutation.addedNodes[i]);
}
})
});
observer.observe(document.getElementById("first"), { childList: true });
var ele = document.createElement('div');
ele.id = "second"
document.getElementById("first").appendChild(ele);
</script>
</body>
</html>
In November 2019, I am seeking a way to create a (hypothetical) onparse EventListener for <elements> which don't take onload.
The (hypothetical) onparse EventListener must be able to listen for when an element is parsed.
Third Attempt (and Definitive Solution)
I was pretty happy with the Second Attempt below, but it just struck me that I can make the code shorter and simpler, by creating a tailor-made event:
let parseEvent = new Event('parse');
This is the best solution yet.
The example below:
Creates a tailor-made parse Event
Declares a function (which can be run at window.onload or any time) which:
Finds any elements in the document which include the attribute data-onparse
Attaches the parse EventListener to each of those elements
Dispatches the parse Event to each of those elements to execute the Callback
Working Example:
// Create (homemade) parse event
let parseEvent = new Event('parse');
// Create Initialising Function which can be run at any time
const initialiseParseableElements = () => {
// Get all the elements which need to respond to an onparse event
let elementsWithParseEventListener = document.querySelectorAll('[data-onparse]');
// Attach Event Listeners and Dispatch Events
elementsWithParseEventListener.forEach((elementWithParseEventListener) => {
elementWithParseEventListener.addEventListener('parse', updateParseEventTarget, false);
elementWithParseEventListener.dataset.onparsed = elementWithParseEventListener.dataset.onparse;
elementWithParseEventListener.removeAttribute('data-onparse');
elementWithParseEventListener.dispatchEvent(parseEvent);
});
}
// Callback function for the Parse Event Listener
const updateParseEventTarget = (e) => {
switch (e.target.dataset.onparsed) {
case ('update-1') : e.target.textContent = 'My First Updated Heading'; break;
case ('update-2') : e.target.textContent = 'My Second Updated Heading'; break;
case ('update-3') : e.target.textContent = 'My Third Updated Heading'; break;
case ('run-oQuickReply.swap()') : e.target.innerHTML = 'This <code><div></code> is now loaded and the function <code>oQuickReply.swap()</code> will run...'; break;
}
}
// Run Initialising Function
initialiseParseableElements();
let dynamicHeading = document.createElement('h3');
dynamicHeading.textContent = 'Heading Text';
dynamicHeading.dataset.onparse = 'update-3';
setTimeout(() => {
// Add new element to page after time delay
document.body.appendChild(dynamicHeading);
// Re-run Initialising Function
initialiseParseableElements();
}, 3000);
div {
width: 300px;
height: 40px;
padding: 12px;
border: 1px solid rgb(191, 191, 191);
}
h3 {
position: absolute;
top: 0;
right: 0;
}
<h2 data-onparse="update-1">My Heading</h2>
<h2 data-onparse="update-2">My Heading</h2>
<div data-onparse="run-oQuickReply.swap()">
This div hasn't yet loaded and nothing will happen.
</div>
Second Attempt
The First Attempt below (based on #JohnWilliams' brilliant Empty Image Hack) used a hardcoded <img /> and worked.
I thought it ought to be possible to remove the hardcoded <img /> entirely and only dynamically insert it after detecting, in an element which needed to fire an onparse event, an attribute like:
data-onparse="run-oQuickReply.swap()"
It turns out, this works very well indeed.
The example below:
Finds any elements in the document which include the attribute data-onparse
Dynamically generates an <img src /> and appends it to the document, immediately after each of those elements
Fires the onerror EventListener when the rendering engine parses each <img src />
Executes the Callback and removes that dynamically generated <img src /> from the document
Working Example:
// Get all the elements which need to respond to an onparse event
let elementsWithParseEventListener = document.querySelectorAll('[data-onparse]');
// Dynamically create and position an empty <img> after each of those elements
elementsWithParseEventListener.forEach((elementWithParseEventListener) => {
let emptyImage = document.createElement('img');
emptyImage.src = '';
elementWithParseEventListener.parentNode.insertBefore(emptyImage, elementWithParseEventListener.nextElementSibling);
});
// Get all the empty images
let parseEventTriggers = document.querySelectorAll('img[src=""]');
// Callback function for the EventListener below
const updateParseEventTarget = (e) => {
let parseEventTarget = e.target.previousElementSibling;
switch (parseEventTarget.dataset.onparse) {
case ('update-1') : parseEventTarget.textContent = 'My First Updated Heading'; break;
case ('update-2') : parseEventTarget.textContent = 'My Second Updated Heading'; break;
case ('run-oQuickReply.swap()') : parseEventTarget.innerHTML = 'This <code><div></code> is now loaded and the function <code>oQuickReply.swap()</code> will run...'; break;
}
// Remove empty image
e.target.remove();
}
// Add onerror EventListener to all the empty images
parseEventTriggers.forEach((parseEventTrigger) => {
parseEventTrigger.addEventListener('error', updateParseEventTarget, false);
});
div {
width: 300px;
height: 40px;
padding: 12px;
border: 1px solid rgb(191, 191, 191);
}
<h2 data-onparse="update-1">My Heading</h2>
<h2 data-onparse="update-2">My Heading</h2>
<div data-onparse="run-oQuickReply.swap()">
This div hasn't yet loaded and nothing will happen.
</div>
First Attempt
I can build on #JohnWilliams' <img src> hack (on this page, from 2017) - which is, so far, the best approach I have come across.
The example below:
Fires the onerror EventListener when the rendering engine parses <img src />
Executes the Callback and removes the <img src /> from the document
Working Example:
let myHeadingLoadEventTrigger = document.getElementById('my-heading-load-event-trigger');
const updateHeading = (e) => {
let myHeading = e.target.previousElementSibling;
if (true) { // <= CONDITION HERE
myHeading.textContent = 'My Updated Heading';
}
// Modern alternative to document.body.removeChild(e.target);
e.target.remove();
}
myHeadingLoadEventTrigger.addEventListener('error', updateHeading, false);
<h2>My Heading</h2>
<img id="my-heading-load-event-trigger" src />
use an iframe and hide it iframe works like a body tag
<!DOCTYPE html>
<html>
<body>
<iframe style="display:none" onload="myFunction()" src="http://www.w3schools.com"></iframe>
<p id="demo"></p>
<script>
function myFunction() {
document.getElementById("demo").innerHTML = "Iframe is loaded.";
}
</script>
</body>
</html>
Since the onload event is only supported on a few elements, you have to use an alternate method.
You can use a MutationObserver for this:
const trackElement = element => {
let present = false;
const checkIfPresent = () => {
if (document.body.contains(element)) {
if (!present) {
console.log('in DOM:', element);
}
present = true;
} else if (present) {
present = false;
console.log('Not in DOM');
}
};
const observer = new MutationObserver(checkIfPresent);
observer.observe(document.body, { childList: true });
checkIfPresent();
return observer;
};
const element = document.querySelector('#element');
const add = () => document.body.appendChild(element);
const remove = () => element.remove();
trackElement(element);
<button onclick="add()">Add</button>
<button onclick="remove()">Remove</button>
<div id="element">Element</div>
we can use all these tags with onload
<body>, <frame>, <frameset>, <iframe>, <img>, <input type="image">, <link>, <script> and <style>
eg:
function loadImage() {
alert("Image is loaded");
}
<img src="https://www.w3schools.com/tags/w3html.gif" onload="loadImage()" width="100" height="132">
I really like the YUI3 library for this sort of thing.
<div id="mydiv"> ... </div>
<script>
YUI().use('node-base', function(Y) {
Y.on("available", someFunction, '#mydiv')
})
See: http://developer.yahoo.com/yui/3/event/#onavailable
This is very simple solution and 100% working.
Just load an <img> tag inside the div or at last line of div, if you think you want to execute javascript, after loading all data in div.
As <img> tag supports onload event, so you can easily call javascript here like below:
<div>
<img onLoad="alert('Problem Solved');" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" />
</div>
This above image will show only a single Dot(.), which you even cant see normally.
Try it.
First to answer your question: No, you can't, not directly like you wanted to do so.
May be a bit late to answer, but this is my solution, without jQuery, pure javascript.
It was originally written to apply a resize function to textareas after DOM is loaded and on keyup.
Same way you could use it to do something with (all) divs or only one, if specified, like so:
document.addEventListener("DOMContentLoaded", function() {
var divs = document.querySelectorAll('div'); // all divs
var mydiv = document.getElementById('myDiv'); // only div#myDiv
divs.forEach( div => {
do_something_with_all_divs(div);
});
do_something_with_mydiv(mydiv);
});
If you really need to do something with a div, loaded after the DOM is loaded, e.g. after an ajax call, you could use a very helpful hack, which is easy to understand an you'll find it ...working-with-elements-before-the-dom-is-ready.... It says "before the DOM is ready" but it works brillant the same way, after an ajax insertion or js-appendChild-whatever of a div. Here's the code, with some tiny changes to my needs.
css
.loaded { // I use only class loaded instead of a nodename
animation-name: nodeReady;
animation-duration: 0.001s;
}
#keyframes nodeReady {
from { clip: rect(1px, auto, auto, auto); }
to { clip: rect(0px, auto, auto, auto); }
}
javascript
document.addEventListener("animationstart", function(event) {
var e = event || window.event;
if (e.animationName == "nodeReady") {
e.target.classList.remove('loaded');
do_something_else();
}
}, false);
I am learning javascript and jquery and was going through all the answer,
i faced same issue when calling javascript function for loading div element.
I tried $('<divid>').ready(function(){alert('test'}) and it worked for me. I want to know is this good way to perform onload call on div element in the way i did using jquery selector.
thanks
As all said, you cannot use onLoad event on a DIV instead but it before body tag.
but in case you have one footer file and include it in many pages. it's better to check first if the div you want is on that page displayed, so the code doesn't executed in the pages that doesn't contain that DIV to make it load faster and save some time for your application.
so you will need to give that DIV an ID and do:
var myElem = document.getElementById('myElementId');
if (myElem !== null){ put your code here}
I had the same question and was trying to get a Div to load a scroll script, using onload or load. The problem I found was that it would always work before the Div could open, not during or after, so it wouldn't really work.
Then I came up with this as a work around.
<body>
<span onmouseover="window.scrollTo(0, document.body.scrollHeight);"
onmouseout="window.scrollTo(0, document.body.scrollHeight);">
<div id="">
</div>
Link to open Div
</span>
</body>
I placed the Div inside a Span and gave the Span two events, a mouseover and a mouseout. Then below that Div, I placed a link to open the Div, and gave that link an event for onclick. All events the exact same, to make the page scroll down to bottom of page. Now when the button to open the Div is clicked, the page will jump down part way, and the Div will open above the button, causing the mouseover and mouseout events to help push the scroll down script. Then any movement of the mouse at that point will push the script one last time.
You could use an interval to check for it until it loads like this:
https://codepen.io/pager/pen/MBgGGM
let checkonloadDoSomething = setInterval(() => {
let onloadDoSomething = document.getElementById("onloadDoSomething");
if (onloadDoSomething) {
onloadDoSomething.innerHTML="Loaded"
clearInterval(checkonloadDoSomething);
} else {`enter code here`
console.log("Waiting for onloadDoSomething to load");
}
}, 100);
When you load some html from server and insert it into DOM tree you can use DOMSubtreeModified however it is deprecated - so you can use MutationObserver or just detect new content inside loadElement function directly so you will don't need to wait for DOM events
var ignoreFirst=0;
var observer = (new MutationObserver((m, ob)=>
{
if(ignoreFirst++>0) {
console.log('Element add on', new Date());
}
}
)).observe(content, {childList: true, subtree:true });
// simulate element loading
var tmp=1;
function loadElement(name) {
setTimeout(()=>{
console.log(`Element ${name} loaded`)
content.innerHTML += `<div>My name is ${name}</div>`;
},1500*tmp++)
};
loadElement('Michael');
loadElement('Madonna');
loadElement('Shakira');
<div id="content"><div>
You can attach an event listener as below. It will trigger whenever the div having selector #my-id loads completely to DOM.
$(document).on('EventName', '#my-id', function() {
// do something
});
Inthis case EventName may be 'load' or 'click'
https://api.jquery.com/on/#on-events-selector-data-handler
Here is a trick that worked for me,
you just need to put your div inside a body element
<body>
<!-- Some code here -->
<body onload="alert('Hello World')">
<div ></div>
</body>
<!-- other lines of code -->
</body>
Use the body.onload event instead, either via attribute (<body onload="myFn()"> ...) or by binding an event in Javascript. This is extremely common with jQuery:
$(document).ready(function() {
doSomething($('#myDiv'));
});
You cannot add event onload on div, but you can add onkeydown and trigger onkeydown event on document load
$(function ()
{
$(".ccsdvCotentPS").trigger("onkeydown");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script>
<div onkeydown="setCss( );"> </div>`
Try this.
document.getElementById("div").onload = alert("This is a div.");
<div id="div">Hello World</div>
Try this one too. You need to remove . from oQuickReply.swap() to make the function working.
document.getElementById("div").onload = oQuickReplyswap();
function oQuickReplyswap() {
alert("Hello World");
}
<div id="div"></div>

addEventListener() to my class with loop

// color pattern function
function colorPattern(color_Pattern_box)
{
var color_Board = $("#colorBoard");
var rtrnVal = ""; //default to standard syntax
var color_temp = ("");
for(var c = 0; c < color_Pattern_box.length; c++){
var color_Split = color_Pattern_box[c].split(":");
var color_Name = color_Split[0];
var color_Value = color_Split[1];
color_temp += "<div id="+color_Value+"; data-title="+color_Name+" onclick='' class='mycolor_warpper'; style='background: "+color_Value+";'></div>";
var color_inset = $(".mycolor_warpper");
colorpattern_attachEvent(color_inset, "click", updateNumberText, JsonSave);
}
// //input option to parent
color_Board.html(color_temp);
}
function colorpattern_attachEvent(element, event, callbackFunction1, callbackFunction2) {
if(typeof(element.addEventListener)=='function') {
element.addEventListener(event, function(){
callbackFunction2(this.getAttribute("id"));
callbackFunction1();
}, false);
}
}
I have function with loop, and I want to to use the loop to add eventListener to my class.
Basically, the loop will create div and class name "mycolor_wrapper". How do I attach each class with addEventListener() click, here is the code I tested it and it doesn't work.
i have function with loop, and I want to to use the loop to add eventlistener to my class.
There's an easier way to go about adding less and getting more, especially with jQuery as Dinglemeyer was explaining.
Basically the loop will create div and class name "mycolor_wrapper". How do I attach each class with addeventlistner click, here is the code i tested it won't work.
Ok, while clumping things together in a code lump might sound great, you should try coding for one portion of your code, test it and see if it works, then move on to the next. Like when you build a house you don't start on the roof and build the foundation then the frame.
So I'll answer this question:
…How do I attach each class with addeventlistner click…
generically with generic JavaScript.
First part of answer, as I have commented (and probably others have as well), you don't want to go that route. Adding multiple eventListeners of the same eventType is unnecessary and messy. Instead you can utilize the properties that will help you control the event bubbling:
event.target: The element that is clicked.
event.currentTarget: The element that you added an eventListener to. In a simple setup, this would be the same as the event.target. But in your situation it'll be different because we will add one eventListener to the parent element (i.e. #colorBoard) of multiple event.targets (i.e. .colorWrap(I hate underscores .colorWrap = .mycolor_warpper)).
event.stopPropagation(): This method will stop the propagation of the click event from bubbling up to the rest of the elements in the event chain (e.g. #colorBoard), thereby we will isolate the click event to just one element.
SNIPPET
// Reference the parent #colorBoard
var parent = document.getElementById('colorBoard');
// Add eventListener to parent
parent.addEventListener('click', trueColor, false);
// Create trueColor(event) eventHandler
function trueColor(event) {
// If event.target is not event.currentTarget then...
if (event.target !== event.currentTarget) {
// Store event.target in the variable 'clicked'
var clicked = event.target;
// Next change clicked background-color to it's id.
clicked.style.backgroundColor = clicked.id;
}
// Use event.stopPropagation() to prevent event bubbling so #colorBoard will never hear the event.
event.stopPropagation();
}
.colorBoard {
border: 3px dashed black;
width: 300px;
height: 300px;
}
h1 {
font-size: 20px;
color: orange;
}
.colorWrap {
height: 100px;
width: 300px;
cursor: pointer;
font-size: 15px;
color: white;
background: black;
border: 1px solid white;
}
<section id="colorBoard">
<h1>Color Board</h1>
<div id="red" class="colorWrap">RED</div>
<div id="green" class="colorWrap">GREEN</div>
<div id="blue" class="colorWrap">BLUE</div>
</section>
<script src="http://gh-canon.github.io/stack-snippet-console/console.min.js"></script>
Is it possible for you to include the JQuery library in your project/site?
You could use a JQuery event handler on the class instead of iterating through objects to attach the event handler?
This would select all objects of your class "my_color_warpper" with a click event handler to call your logic
$(".mycolor_warpper").on ("click", function () {
// your logic
callbackFunction2(this.getAttribute("id"));
callbackFunction1();
});
Rather than looping through by adding event handler in this way you only declare it once and it slams them all out.

Is this event listener reaching the top most element?

I am developing a page that will create many <div>s and appending them to a container <div>. I need to know which one of them is being clicked on. At first, I figured putting an eventListener on each one of them would be fine. However, I just read an article about using Smart Event Handlers here: Best Practices for Speeding Up Your Web Site. And it talks about using only 1 eventListener and figuring out which one of the elements the event originates from.
EDIT
I removed the previous example, here is a small example that is much closer to my target functionality and illustrates why it would be preferable to have one listener that dispatches what was clicked on. The main thing is that the <div> is what knows which index in an array needs to be grabbed for data. However, the data gets presented in <div>s that are inside the <div> that knows the array index and the event doesn't hook with him for some reason.
When you run it, you see that the log only lists the contact whenever the green "middle" <div> gets clicked but not the red "information" <div>s. How can I get the red information <div>s to trigger the listener as well without adding a zillion listeners?
JSFiddle
<!DOCTYPE html>
<html>
<head>
<title>Bubbling</title>
<meta charset="UTF-8">
<style>
div{
margin: 10px;
padding: 10px;
width: 200px;
}
#big{
background: #ccffcc;
}
.contactDiv{
background: #99ff99;
}
.nameDiv, .phoneDiv{
background: #ff9999;
}
#log{
background: #ccccff;
}
</style>
</head>
<body>
<div id="log">I log stuff</div>
<button id="adder">Add Some Div</button>
<!--Highest div that encloses multiple other div-->
<div id="big"></div>
<script>
var log = document.getElementById("log"),
adderButton = document.getElementById("adder"),
bigDiv = document.getElementById("big"),
numDivsToMake = 100,
i, contactDiv, nameDiv, phoneDiv,
contacts = [];
for (i = 0; i < numDivsToMake; i++) {
contacts.push({
name: "Bob-" + i,
phone: "555-1234"
});
}
// Make more divs whenever we click the super button
adderButton.addEventListener("click", function() {
// Don't make more
adderButton.setAttribute("disabled");
// Make the divs with data
for (i = 0; i < numDivsToMake; i++) {
// Make name and number divs
contactDiv = document.createElement("div");
nameDiv = document.createElement("div");
phoneDiv = document.createElement("div");
// Add classes
contactDiv.className = "contactDiv";
nameDiv.className = "nameDiv";
phoneDiv.className = "phoneDiv";
// Set their values
nameDiv.innerHTML = contacts[i].name;
phoneDiv.innerHTML = contacts[i].phone;
// Set the container to know how to get back to the data in the array
contactDiv.setAttribute("data-contactId", i);
// Add them to the dom
bigDiv.appendChild(contactDiv);
contactDiv.appendChild(nameDiv);
contactDiv.appendChild(phoneDiv);
}
});
// Make smart handler
bigDiv.addEventListener("click", function(e) {
// Get whether the element has the attribute we want
var att = e.target.getAttribute("data-contactId");
// Say if it does or not
if (att) {
log.innerHTML = "You clicked: " +
contacts[att].name + " " +
contacts[att].phone;
}
else {
console.log("No attribute");
}
});
</script>
</body>
</html>
I think I understand what you're doing. The event delegation you've set up in the fiddle seems to work well. The data attribute you want to select could easily be selected inside your click handler by querying the DOM. Instead of looking at the source of the click if you know you always want the data, just do another document.getElementById to retrieve your data. I think you're trying to collapse two steps into one in a way that won't work with your design.

Categories