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

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>

Related

How can I hide a div that I've just added with a click?

I'm appending some HTML to my button on a click, like this:
jQuery(document).ready(function($) {
$('#sprout-view-grant-access-button').on('click', function(e) {
$(this).toggleClass('request-help-cta-transition', 1000, 'easeOutSine');
var callback = $(e.currentTarget).attr('data-grant-access-callback');
var wrapper = $('.dynamic-container');
console.log(wrapper);
if( typeof window[callback] !== 'function') {
console.log('Callback not exist: %s', callback);
}
var already_exists = wrapper.find('.main-grant-access');
console.log(already_exists);
if( already_exists.length ) {
already_exists.remove();
}
var markup = $(window[callback](e.currentTarget));
wrapper.append(markup);
});
});
function generate_grant_access_container_markup() {
var contact_data_array = contact_data;
var template = jQuery('#template-sprout-grant-access-container')
return mustache(template.html(), {
test: 's'
});
}
As per the code, whatever comes from generate_grant_access_container_markup will be put inside dynamic-container and shown.
My problem is that, the newly added code just doesn't wanna dissapear upon clicking (toggle) of the button once again.
Here's my syntax / mustache template:
<script type="template/mustache" id="template-sprout-grant-access-container">
<p class="main-grant-access">{{{test}}}</p>
</script>
And here's the container:
<div class="button-nice request-help-cta" id="sprout-view-grant-access-button" data-grant-access-callback="generate_grant_access_container_markup">
Grant Devs Access
<div class="dynamic-container"></div>
</div>
I understand that the click event only knows about items that are in the DOM at the moment of the click, but how can I make it aware of everything that gets added after?
I would recommend visibility: hidden. Both display none and removing elements from the dom mess with the flow of the website. You can be sure you would not affect the design with visibility: hidden.
I don't deal with Jquery at all but it seems like this Stack overflow covers the method to set it up well.
Equivalent of jQuery .hide() to set visibility: hidden

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";
});
}

ng-mouseover ng-mouseout not woking

below is my code, I'm trying to make the content wrapped in div tag change the background color when the mouse curse over it, if the one of the content's variable starts with *. But it doesn't work...
// html
<style>
.normal{background-color: white}
.change{background-color: gainsboro}
</style>
<div ng-mouseover="checkAs(this)" ng-mouseout="this.className='normal'">
......
</div>
// js
$scope.checkAs = function(obj) {
var name = $scope.opportunity.name;
var asterisk = '*';
if(name.startsWith(asterisk)) {
obj.className='change';
} else {
obj.className='normal';
}
};
If you are determined to do this in angular, you would have to call a function through ng-mouseover and in that function, you would need a selector such as JQuery or Javascript's query selector, then modify the element as you see fit. You would have to do something like this (using JQuery):
$scope.checkAs = function() {
$("div").hover(function() {
$(this).prop('background-color','gainsboro');
}, function(){
$(this).prop('background-color','white');
});
};
But, as PSL suggested, the "this" in checkAs(this) won't be the DOM element. A CSS solution might be better:
div :hover{
background-color: gainsboro
}

jQuery animation not working in anchor tags or anchor tag children

I've spent the better part of a day tracking down a problem I've been having with jQuery animation. There appear to be issues with applying jQuery.animate() to anchor elements, or to child elements inside of anchor elements, at least with regard to movement animations. I've boiled the problem down to a fairly simple example which illustrates the problem:
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8/jquery.min.js"></script>
<script>
var foo = {};
function TestMove(newx, newy) {
this.newx = newx;
this.newy = newy;
}
TestMove.prototype = {
movex:function () {
$("#newsec").animate({left: this.newx + "px"});
},
movey:function () {
$("#newsec").animate({top: this.newy + "px"});
}
}
function bar() {
foo[1].movex();
foo[1].movey();
}
function init() {
foo[1] = new TestMove(200,200);
}
</script>
</head>
<body onload="init()">
<a href="" style="position: relative;">
<div style="position: relative; height: 50px; width: 50px; background-color: red;" id="newsec" onclick="bar()"></div>
</a>
</body>
</html>
The animation doesn't work, regardless of whether I put the id attribute and onclick event handler call in the <a> tag or in the <div> within it. If, on the other hand,I remove the <a> element tags altogether, the animation works as expected on the <div> element.
Does anyone have any idea why this happens?
The issue is almost moot, since I can easily do with <div> elements in the working page what I could also do with <a> elements. In the working code (much more complex) I'm using event.preventDefault() on the anchor elements so that linking and other actions are driven by explicit event handlers and this can be done from a <div> just as well. I believe I can even change the pointer icon when one does a mouseover on the <div> so that it mimics a true anchor in this regard as well.
It's because the browser is going to the anchor prior to the animation being put in place. There are plugins to get around these sort of issues, or you can put together your own.
http://briangonzalez.org/arbitrary-anchor
Example of a simple implementation:
jQuery.fn.anchorAnimate = function(settings) {
settings = jQuery.extend({
speed : 1100
}, settings);
return this.each(function(){
var caller = this
$(caller).click(function (event) {
event.preventDefault()
var locationHref = window.location.href
var elementClick = $(caller).attr("href")
var destination = $(elementClick).offset().top;
$("html:not(:animated),body:not(:animated)").animate({ scrollTop: destination}, settings.speed, function() {
window.location.hash = elementClick
});
return false;
})
})
}

Prevent onmouseout when hovering child element of the parent absolute div WITHOUT jQuery

I am having trouble with the onmouseout function in an absolute positoned div. When the mouse hits a child element in the div, the mouseout event fires, but I do not want it to fire until the mouse is out of the parent, absolute div.
How can I prevent the mouseout event from firing when it hits a child element WITHOUT jquery.
I know this has something to do with event bubbling, but I am having no luck on finding out how to work this out.
I found a similar post here: How to disable mouseout events triggered by child elements?
However that solution uses jQuery.
Use onmouseleave.
Or, in jQuery, use mouseleave()
It is the exact thing you are looking for. Example:
<div class="outer" onmouseleave="yourFunction()">
<div class="inner">
</div>
</div>
or, in jQuery:
$(".outer").mouseleave(function(){
//your code here
});
an example is here.
For a simpler pure CSS solution that works in most cases, one could remove children's pointer-events by setting them to none
.parent * {
pointer-events: none;
}
Browser support: IE11+
function onMouseOut(event) {
//this is the original element the event handler was assigned to
var e = event.toElement || event.relatedTarget;
if (e.parentNode == this || e == this) {
return;
}
alert('MouseOut');
// handle mouse event here!
}
document.getElementById('parent').addEventListener('mouseout',onMouseOut,true);
I made a quick JsFiddle demo, with all the CSS and HTML needed, check it out...
EDIT FIXED link for cross-browser support http://jsfiddle.net/RH3tA/9/
NOTE that this only checks the immediate parent, if the parent div had nested children then you have to somehow traverse through the elements parents looking for the "Orginal element"
EDIT example for nested children
EDIT Fixed for hopefully cross-browser
function makeMouseOutFn(elem){
var list = traverseChildren(elem);
return function onMouseOut(event) {
var e = event.toElement || event.relatedTarget;
if (!!~list.indexOf(e)) {
return;
}
alert('MouseOut');
// handle mouse event here!
};
}
//using closure to cache all child elements
var parent = document.getElementById("parent");
parent.addEventListener('mouseout',makeMouseOutFn(parent),true);
//quick and dirty DFS children traversal,
function traverseChildren(elem){
var children = [];
var q = [];
q.push(elem);
while (q.length > 0) {
var elem = q.pop();
children.push(elem);
pushAll(elem.children);
}
function pushAll(elemArray){
for(var i=0; i < elemArray.length; i++) {
q.push(elemArray[i]);
}
}
return children;
}
And a new JSFiddle, EDIT updated link
instead of onmouseout use onmouseleave.
You haven't showed to us your specific code so I cannot show you on your specific example how to do it.
But it is very simple: just replace onmouseout with onmouseleave.
That's all :) So, simple :)
If not sure how to do it, see explanation on:
https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_onmousemove_leave_out
Peace of cake :) Enjoy it :)
Here's a more elegant solution based on what came below.
it accounts for event bubbling up from more than one level of children.
It also accounts for cross-browser issues.
function onMouseOut(this, event) {
//this is the original element the event handler was assigned to
var e = event.toElement || event.relatedTarget;
//check for all children levels (checking from bottom up)
while(e && e.parentNode && e.parentNode != window) {
if (e.parentNode == this|| e == this) {
if(e.preventDefault) e.preventDefault();
return false;
}
e = e.parentNode;
}
//Do something u need here
}
document.getElementById('parent').addEventListener('mouseout',onMouseOut,true);
Thanks to Amjad Masad that inspired me.
I've the following solution which seems to work in IE9, FF and Chrome and the code is quite short (without the complex closure and transverse child things) :
DIV.onmouseout=function(e){
// check and loop relatedTarget.parentNode
// ignore event triggered mouse overing any child element or leaving itself
var obj=e.relatedTarget;
while(obj!=null){
if(obj==this){
return;
}
obj=obj.parentNode;
}
// now perform the actual action you want to do only when mouse is leaving the DIV
}
If you're using jQuery you can also use the "mouseleave" function, which deals with all of this for you.
$('#thetargetdiv').mouseenter(do_something);
$('#thetargetdiv').mouseleave(do_something_else);
do_something will fire when the mouse enters thetargetdiv or any of its children, do_something_else will only fire when the mouse leaves thetargetdiv and any of its children.
I think Quirksmode has all the answers you need (different browsers bubbling behaviour and the mouseenter/mouseleave events), but I think the most common conclusion to that event bubbling mess is the use of a framework like JQuery or Mootools (which has the mouseenter and mouseleave events, which are exactly what you intuited would happen).
Have a look at how they do it, if you want, do it yourself
or you can create your custom "lean mean" version of Mootools with just the event part (and its dependencies).
Try mouseleave()
Example :
<div id="parent" mouseleave="function">
<div id="child">
</div>
</div>
;)
I've found a very simple solution,
just use the onmouseleave="myfunc()" event than the onmousout="myfunc()" event
In my code it worked!!
Example:
<html>
<head>
<script type="text/javascript">
function myFunc(){
document.getElementById('hide_div').style.display = 'none';
}
function ShowFunc(){
document.getElementById('hide_div').style.display = 'block';
}
</script>
</head>
<body>
<div onmouseleave="myFunc()" style='border:double;width:50%;height:50%;position:absolute;top:25%;left:25%;'>
Hover mouse here
<div id='child_div' style='border:solid;width:25%;height:25%;position:absolute;top:10%;left:10%;'>
CHILD <br/> It doesn't fires if you hover mouse over this child_div
</div>
</div>
<div id="hide_div" >TEXT</div>
Show "TEXT"
</body>
</html>
Same Example with mouseout function:
<html>
<head>
<script type="text/javascript">
function myFunc(){
document.getElementById('hide_div').style.display = 'none';
}
function ShowFunc(){
document.getElementById('hide_div').style.display = 'block';
}
</script>
</head>
<body>
<div onmouseout="myFunc()" style='border:double;width:50%;height:50%;position:absolute;top:25%;left:25%;'>
Hover mouse here
<div id='child_div' style='border:solid;width:25%;height:25%;position:absolute;top:10%;left:10%;'>
CHILD <br/> It fires if you hover mouse over this child_div
</div>
</div>
<div id="hide_div">TEXT</div>
Show "TEXT"
</body>
</html>
Hope it helps :)
Although the solution you referred to uses jquery,
mouseenter and mouseleave are native dom events, so you might use without jquery.
There are two ways to handle this.
1) Check the event.target result in your callback to see if it matches your parent div
var g_ParentDiv;
function OnMouseOut(event) {
if (event.target != g_ParentDiv) {
return;
}
// handle mouse event here!
};
window.onload = function() {
g_ParentDiv = document.getElementById("parentdiv");
g_ParentDiv.onmouseout = OnMouseOut;
};
<div id="parentdiv">
<img src="childimage.jpg" id="childimg" />
</div>
2) Or use event capturing and call event.stopPropagation in the callback function
var g_ParentDiv;
function OnMouseOut(event) {
event.stopPropagation(); // don't let the event recurse into children
// handle mouse event here!
};
window.onload = function() {
g_ParentDiv = document.getElementById("parentdiv");
g_ParentDiv.addEventListener("mouseout", OnMouseOut, true); // pass true to enable event capturing so parent gets event callback before children
};
<div id="parentdiv">
<img src="childimage.jpg" id="childimg" />
</div>
simply we can check e.relatedTarget has child class and if true return the function.
if ($(e.relatedTarget).hasClass("ctrl-btn")){
return;
}
this is code worked for me, i used for html5 video play,pause button toggle hover video element
element.on("mouseover mouseout", function(e) {
if(e.type === "mouseout"){
if ($(e.relatedTarget).hasClass("child-class")){
return;
}
}
});
I make it work like a charm with this:
function HideLayer(theEvent){
var MyDiv=document.getElementById('MyDiv');
if(MyDiv==(!theEvent?window.event:theEvent.target)){
MyDiv.style.display='none';
}
}
Ah, and MyDiv tag is like this:
<div id="MyDiv" onmouseout="JavaScript: HideLayer(event);">
<!-- Here whatever divs, inputs, links, images, anything you want... -->
<div>
This way, when onmouseout goes to a child, grand-child, etc... the style.display='none' is not executed; but when onmouseout goes out of MyDiv it runs.
So no need to stop propagation, use timers, etc...
Thanks for examples, i could make this code from them.
Hope this helps someone.
Also can be improved like this:
function HideLayer(theLayer,theEvent){
if(theLayer==(!theEvent?window.event:theEvent.target)){
theLayer.style.display='none';
}
}
And then the DIVs tags like this:
<div onmouseout="JavaScript: HideLayer(this,event);">
<!-- Here whatever divs, inputs, links, images, anything you want... -->
<div>
So more general, not only for one div and no need to add id="..." on each layer.
If you have access to the element which the event is attached to inside the mouseout method, you can use contains() to see if the event.relatedTarget is a child element or not.
As event.relatedTarget is the element to which the mouse has passed into, if it isn't a child element, you have moused out of the element.
div.onmouseout = function (event) {
if (!div.contains(event.relatedTarget)) {
// moused out of div
}
}
On Angular 5, 6 and 7
<div (mouseout)="onMouseOut($event)"
(mouseenter)="onMouseEnter($event)"></div>
Then on
import {Component,Renderer2} from '#angular/core';
...
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss']
})
export class TestComponent implements OnInit, OnDestroy {
...
public targetElement: HTMLElement;
constructor(private _renderer: Renderer2) {
}
ngOnInit(): void {
}
ngOnDestroy(): void {
//Maybe reset the targetElement
}
public onMouseEnter(event): void {
this.targetElement = event.target || event.srcElement;
console.log('Mouse Enter', this.targetElement);
}
public onMouseOut(event): void {
const elementRelated = event.toElement || event.relatedTarget;
if (this.targetElement.contains(elementRelated)) {
return;
}
console.log('Mouse Out');
}
}
I check the original element's offset to get the page coordinates of the element's bounds, then make sure the mouseout action is only triggered when the mouseout is out of those bounds. Dirty but it works.
$(el).live('mouseout', function(event){
while(checkPosition(this, event)){
console.log("mouseovering including children")
}
console.log("moused out of the whole")
})
var checkPosition = function(el, event){
var position = $(el).offset()
var height = $(el).height()
var width = $(el).width()
if (event.pageY > position.top
|| event.pageY < (position.top + height)
|| event.pageX > position.left
|| event.pageX < (position.left + width)){
return true
}
}
var elem = $('#some-id');
elem.mouseover(function () {
// Some code here
}).mouseout(function (event) {
var e = event.toElement || event.relatedTarget;
if (elem.has(e).length > 0) return;
// Some code here
});
If you added (or have) a CSS class or id to the parent element, then you can do something like this:
<div id="parent">
<div>
</div>
</div>
JavaScript:
document.getElementById("parent").onmouseout = function(e) {
e = e ? e : window.event //For IE
if(e.target.id == "parent") {
//Do your stuff
}
}
So stuff only gets executed when the event is on the parent div.
I just wanted to share something with you.
I got some hard time with ng-mouseenter and ng-mouseleave events.
The case study:
I created a floating navigation menu which is toggle when the cursor is over an icon.
This menu was on top of each page.
To handle show/hide on the menu, I toggle a class.
ng-class="{down: vm.isHover}"
To toggle vm.isHover, I use the ng mouse events.
ng-mouseenter="vm.isHover = true"
ng-mouseleave="vm.isHover = false"
For now, everything was fine and worked as expected.
The solution is clean and simple.
The incoming problem:
In a specific view, I have a list of elements.
I added an action panel when the cursor is over an element of the list.
I used the same code as above to handle the behavior.
The problem:
I figured out when my cursor is on the floating navigation menu and also on the top of an element, there is a conflict between each other.
The action panel showed up and the floating navigation was hide.
The thing is that even if the cursor is over the floating navigation menu, the list element ng-mouseenter is triggered.
It makes no sense to me, because I would expect an automatic break of the mouse propagation events.
I must say that I was disappointed and I spend some time to find out that problem.
First thoughts:
I tried to use these :
$event.stopPropagation()
$event.stopImmediatePropagation()
I combined a lot of ng pointer events (mousemove, mouveover, ...) but none help me.
CSS solution:
I found the solution with a simple css property that I use more and more:
pointer-events: none;
Basically, I use it like that (on my list elements):
ng-style="{'pointer-events': vm.isHover ? 'none' : ''}"
With this tricky one, the ng-mouse events will no longer be triggered and my floating navigation menu will no longer close himself when the cursor is over it and over an element from the list.
To go further:
As you may expect, this solution works but I don't like it.
We do not control our events and it is bad.
Plus, you must have an access to the vm.isHover scope to achieve that and it may not be possible or possible but dirty in some way or another.
I could make a fiddle if someone want to look.
Nevertheless, I don't have another solution...
It's a long story and I can't give you a potato so please forgive me my friend.
Anyway, pointer-events: none is life, so remember it.
There are a simple way to make it work. The element and all childs you set a same class name, then:
element.onmouseover = function(event){
if (event.target.className == "name"){
/*code*/
}
}
Also for vanillajs you can use that way
document.querySelector('.product_items') && document.querySelector('.product_items').addEventListener('mouseleave', () => updateCart())
const updateCart = () => {
let total = 0;
document.querySelectorAll('input') && document.querySelectorAll('input').forEach(item => total += +item.value)
document.getElementById('total').innerHTML = total
}
<div class="product_items">
<div class="product_item">
<div class="product_name">
</div>
<div class="multiply__btn">
<button type="button">-</button>
<input name="test" type="text">
<button type="button">+</button>
</div>
</div>
<div class="product_item">
<div class="product_name">
</div>
<div class="multiply__btn">
<button type="button">-</button>
<input name="test" type="text">
<button type="button">+</button>
</div>
</div>
<div class="product_item">
<div class="product_name">
</div>
<div class="multiply__btn">
<button type="button">-</button>
<input name="test" type="text">
<button type="button">+</button>
</div>
</div>
</div>
<div id="total"></div>
If for some reason you don't want to use the mouseenter and mouseleave events, you can use mouseover/mouseout with a little "debouncing".
The idea relies on the fact that your event handler will receive out followed by a new over when crossing boundaries between various child elements....except when the mouse has really left (for longer than the debounce period). This seems simpler than crawling the dom nodes on every event.
If you "debounce" with a short delay before assuming you have a real out you can effectively ignore all these out/over events bubbling up from child elements.
Note! This will not work if a child element also has a listener for over and/or out events AND their handler calls event.stopPropogation() to stop the event from bubbling up to the parent element where we have attached our handler. If you control the code, this is not necessarily a problem, but you should be aware.
sample code
javascript
function mouseOverOutDebounce (element, debounceMs, mouseOverFn, mouseOutFn) {
var over = false,
debounceTimers = [];
function mouseOver (evt) {
if (over) { // already OVER, existing interaction
while (debounceTimers.length > 0) { // then we had a pending mouseout(s), cancel
window.clearTimeout(debounceTimers.shift());
}
}
else { // new OVER
over = true;
mouseOverFn(evt);
}
}
function mouseOut (evt) {
if (!over) return; // already OUT, ignore.
debounceTimers.push(window.setTimeout(function () {
over = false;
mouseOutFn(evt);
}, debounceMs));
}
function removeEventListeners () {
element.removeEventListener('mouseover', mouseOver);
element.removeEventListener('mouseout', mouseOut);
}
element.addEventListener('mouseover', mouseOver);
element.addEventListener('mouseout', mouseOut);
return removeEventListeners;
}
var someEl = document.querySelector('.container'),
textarea = document.querySelector('textarea'),
mouseOver = function (evt) { report('mouseOVER', evt); },
mouseOut = function (evt) { report('mouseOUT', evt); },
removeEventListeners = mouseOverOutDebounce(someEl, 200, mouseOver, mouseOut);
function report(msg, data) {
console.log(msg, data);
textarea.value = textarea.value + msg + '\n';
}
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
html, body {
margin: 0;
padding: 0;
}
body {
margin: 5%;
}
.container {
width: 300px;
height: 600px;
border: 10px solid red;
background-color: #dedede;
float: left;
}
.container .square {
width: 100px;
height: 100px;
background-color: #2086cf;
margin: -10px 0 0 -10px;
}
textarea {
margin-left: 50px;
width: 800px;
height: 400px;
background-color: #464646;
font-family: monospace;
color: white;
}
.bar {
width: 2px;
height: 30px;
display: inline-block;
margin-left: 2px;
background-color: pink;
}
</style>
</head>
<body>
<div class="container">
<div class="square"></div>
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
</div>
<textarea></textarea>
<script src="interactions.js"></script>
</body>
</html>
fiddle
https://jsfiddle.net/matp/9bhjkLct/5/

Categories