I have a layout which I dont think its necessary to post here. But its basically a parent div with a very small width and height that stays on bottom right of a page, if clicked on it, it enlarges width and height.
Here where I had issue:
The parent contains onclick, if its click, it then change its size as well as disabling its onclick and adding onclick to an image, which is a button to minimise the parent div to its original size.
If the image is clicked then again, I disable the onclick on the image and add onclick on the main parent. However Javascript is executing onclick on the parent as it thinks its been clicked, since the image is inside the parent.
To fix this issue I had to set a setTimeout to add the onclick to the parent, that solved the issue, but is there a better way, and why does that happen?
Tahnks
function adjust_window_displayed(el, main_el){
var val = el.value;
var img_el = document.getElementById("img_envelope");
if(val === "true"){
document.getElementById("display_mail_info").style.display = "block";
img_el.src = "https://co.uk/minimise.png";
img_el.style.cursor = "pointer";
main_el.style.cursor = "default";
main_el.onclick = function () {
return false;
};
el.value = "false";
img_el.onclick = function () {
adjust_window_displayed(el, main_el);
console.log("first");
};
console.log("first " + main_el.className);
}else{
document.getElementById("display_mail_info").style.display = "none";
img_el.src = "https://dco.uk/envelope.png";
img_el.style.cursor = "default";
main_el.style.cursor = "pointer";
el.value = "true";
img_el.onclick = function () {
return false;
};
setTimeout(function() {
main_el.onclick = function () {
adjust_window_displayed(el, main_el);
console.log("sec");
};
}, 700);
console.log("second " + main_el.className);
}
}
What happens is due to the event bubble, when you click on the <img> the click event fire once, then a second time for the parent, now if you don't handle that in your function properly it'll give you problems.
Now I have no idea how you're calling that function nor what parameters, you're passing to it, so i tried to reproduce the problem myself.
If you would assign the click event useing the .onclick = function(){}
you can use it to alter between the two, since you're using the same function.
document.querySelector('#parent').onclick = lol;
function lol(e){
var type = e.target.nodeName;
if (type == "DIV")
{
if (e.target.onclick)
{
alert('clicked parent');
e.target.onclick = "";
document.querySelector('#kid').onclick = lol;
}
}
else if (type == "IMG")
{
if (e.target.onclick)
{
alert('clicked kid');
e.target.onclick = "";
document.querySelector('#parent').onclick = lol;
}
}
}
#parent{
width: 500px;
height: 500px;
background-color: red;
}
img{
width: 10%;
height: 10%;
}
<div id="parent">
<img id="kid">
</div>
I am implementing a JS Event-Disabler class, to disable all Native and Programmable eventlisteners of a certain dom element and all its children.
So far I've been able to disable all JQuery events and the default browser events, but not the eventlisteners set like
document.getElementById('cin').addEventListener("click", function(){
alert('I should not alert when disabled');
});
So clicking on the element ('native element') shouldn't alert, but it does.
How do I stop that from happening, within my nothing function.
If there is away to not even need to call another function but just disable all events then that would also be fine, but need to be able to re-enable all again.
Also, I can assure you that the nothing() function executes first.
var tellme = function(who) {
//console.info('Event by: '+who+' #'+Date.now());
alert('Event by: ' + who + ' #' + Date.now());
}
$(window).load(function() {
/* SOME FUNCTION TO ENSURE OUR FUNCTIONS ARE THE FIRST TO BE CALLED */
$.fn.bindFirst = function(name, fn) {
this.on(name, fn);
this.each(function() {
var handlers = $._data(this, 'events');
for (var key in handlers) {
if (handlers.hasOwnProperty(key)) {
var listeners = handlers[key];
if (listeners.length > 1) {
var lastEvent = listeners.pop();
listeners.splice(0, 0, lastEvent);
if (listeners[1].handler.name === lastEvent.handler.name)
listeners.splice(1, 1);
}
}
}
});
};
function shouldbenothing() {
tellme('native catcher');
nothing();
}
/* THE DO NOTHING FUNCTION, NEEDS SOMETHING MORE, DOESN'T CANCEL ALL*/
function nothing() {
event.cancel = true;
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
//Needed for Jquery
throw new Error("NOT AN ERROR: Just forcefully stopping further events #" /*+Date.now()*/ ); //Add the Date.now to see that this code does run before the native function.
return false;
}
/* THIS WILL ONLY RETURN NON-NATIVE EVENTS, ONLY PROGRAMMED EVENTS*/
function getAllActiveEvents(element) {
var result = [];
var handlers = $._data(element, 'events');
for (var key in handlers) {
if (handlers.hasOwnProperty(key)) {
result.push(key);
}
}
return result.join(' ');
}
function getAllEvents(element) {
var result = [];
for (var key in element) {
if (key.indexOf('on') === 0) {
result.push(key.slice(2));
}
}
return result.join(' ');
}
/*SOME PROGRAMMED EVENTS, BESIDES THE NATIVE ONES*/
$('input').on('keyup', function() {
$('#text').html(this.value);
});
$('p').on('click', function() {
$('#text').html(this.innerHTML);
tellme('jquery');
});
document.getElementById('jsE').addEventListener("click", function() {
tellme('p:js');
});
document.getElementById('cin').addEventListener("click", function() {
tellme('input:js');
});
/* THE ACTUAL DISABLER CODE */
/*TOGGLE TO ACTIVE OR DISABLE EVENTS FROM TAKING PLACE NATIVE AND EXTRA*/
var isOn = false;
$('button').on('click', function() {
if (isOn)
$("#obj *").each(function() {
$(this).off(getAllEvents($(this)[0]), "", nothing);
$("#obj").css('pointerEvents','');
});
else {
$("#obj *").each(function() {
var elem = $(this)[0];
var events1 = getAllActiveEvents(elem); //Only programmed listeners
var events2 = getAllEvents(elem); //Native + other listeners
$(this).bindFirst(events2, nothing);
});
$("#obj").css('pointerEvents','none');
}
isOn = !isOn;
this.innerHTML = isOn;
});
});
p {
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<style>p {pointer:hand;}</style>
<div id="obj">
<p>jquery event</p>
<p id="jsE">js event</p>
<p onclick="tellme('native');">native event</p>
<input id='cin' type="text" />
<p id="text">3</p>
</div>
<p>not catched</p>
<input type="text">
<button>toggle</button>
There might be a very simple, non-js, pure css-solution ... like this:
.whatever {
-webkit-user-select:none;
-moz-user-select:none;
-ms-user-select:none;
user-select:none;
pointer-events:none;
}
... just add the whatever-class to any elements you want to disable completely from user-interaction.
So I found a solution shortly after.
By playing with the css code, I could disable all the relevant mouse events. This however doesn't stop the native events, say if you were to trigger the event via JS, but at least it stops it from user's point.
I actually also like the css method better, as it does allow me to still interact and trigger events, for instance when I want to show the user something without having the user interfere.
The css code:
//To Disable
$("#obj").css('pointerEvents','none');
//To Enable
$("#obj").css('pointerEvents','');
For anyone looking for the full Working Code: Here it is.
Make sure you add the css.
/* Event Disabler, disables all events */
/* How to use:
* Toggle Events: toggleEvents(selector);
* Disable all Events: toggleEvents('body',true);
* Enable all Events: toggleEvents('body',false);
*/
var toggleEvents = null;
$(window).load(function(){
/* SOME FUNCTION TO ENSURE OUR FUNCTIONS ARE THE FIRST TO BE CALLED */
$.fn.bindFirst = function(name, fn) {
this.on(name, fn);
this.each(function() {
var handlers = $._data(this, 'events');
for (var key in handlers) {
if (handlers.hasOwnProperty(key)) {
var listners = handlers[key];
if (listners.length > 1) {
var lastEvent = listners.pop();
listners.splice(0, 0, lastEvent);
//Removes duplicate eventListners
if (listners[1].handler.name === lastEvent.handler.name)
listners.splice(1, 1);
}
}
}
});
};
/* THE DO NOTHING FUNTION CANCELS ALL EVENTS, EVEN BY TRIGGERED*/
function nothing() {
event.cancel = true;
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
event.bubbles = false;
if(window.event){
window.event.cancelBubble=true;
}
//throw new Error("NOT AN ERROR: Forcefully stopping further events");
return false;
}
function getAllActiveEvents(element) {
var result = [];
var handlers = $._data(element, 'events');
for (var key in handlers) {
if (handlers.hasOwnProperty(key)) {
result.push(key);
}
}
return result.join(' ');
}
function getAllEvents(element) {
var result = [];
for (var key in element) {
if (key.indexOf('on') === 0) {
result.push(key.slice(2));
}
}
return result.join(' ');
}
var enabled = false;
toggleEvents = function(selector,flag) {
enabled = flag === undefined ? !enabled : flag;
if (enabled) {
$(selector+" *").each(function(){
//Only programmed and attached listners
var events1 = getAllActiveEvents($(this)[0]);
//All Native events attached or not
var events2 = getAllEvents($(this)[0]);
$(this).bindFirst(events2, nothing );
});
//Disabled most user pointer events
$(selector).addClass('eventsDisabled');
} else {
$(selector+" *").each(function() {
$(this).off(getAllEvents($(this)[0]), "", nothing );
});
$(selector).removeClass('eventsDisabled');
}
};
});
.eventsDisabled {
-webkit-user-select:none !important;
-moz-user-select:none !important;
-ms-user-select:none !important;
user-select:none !important;
pointer-events:none !important;
}
I would like to create a clean solution for handling missing image on the client
using <img src="image.gif" onerror="handleErrors()">
so far the handleErrors looks like this:
function handleErrors() {
image.onerror = "";
image.src = "/images/noimage.gif";
return true;
}
But I feel this is not scalable enough and the no image is also not accessible for screen readers.
What could be a more scalable and accessible solution for this problem?
Try using the alt text attribute for your images.
They are more accessible for screen readers.
Also you can create a module which on error hides the images and
replaces them with their alt text
Here is a module I wrote for handling such issues:
function missingImagesHandler() {
var self = this;
// get all images on the page
self.pageImages = document.querySelectorAll("img");
self.ImageErrorHandler = function (event) {
// hide them
event.target.style.display = 'none';
// replace them with alt text
self.replaceAltTextWithImage(event.target);
}
self.replaceAltTextWithImage = function (imageElement) {
var altText = imageElement.getAttribute("alt");
if (altText) {
var missingLabel = document.createElement("P");
var textnode = document.createTextNode(altText);
missingLabel.appendChild(textnode)
imageElement.parentNode.insertBefore(missingLabel, imageElement);
} else {
console.error(imageElement, "is missing alt text");
}
}
self.attachErrorHandler = function () {
self.pageImages.forEach(function (img) {
img.addEventListener("error", self.ImageErrorHandler);
});
}
self.init = function () {
// NodeList doesn't have forEach by default
NodeList.prototype.forEach = Array.prototype.forEach;
self.attachErrorHandler();
}
return {
init: self.init
}
}
var ImgHandler = new missingImagesHandler();
ImgHandler.init();
HTML:
<img id="myImg" src="image.gif">
JavaScript:
document.getElementById("myImg").onerror = handleErrors();
function handleErrors() {
document.getElementById("myImg").src = "http://blog.stackoverflow.com/wp-content/uploads/stackoverflow-logo-300.png";
return true;
}
Give an ID to image and use this for call your function:
document.getElementById("myImg").onerror = handleErrors();
Giving an ID is more suitable way.
I have a div that I'm appending to another div when a button is clicked. I'm also calling a bunch of functions on the div that gets created.
HTML
<a onClick="drawRect();">Rect</a>
JS
function drawRect(){
var elemRect = document.createElement('div');
elemRect.className = 'elem elemRect';
elemRect.style.position = "absolute";
elemRect.style.background = "#ecf0f1";
elemRect.style.width = "100%";
elemRect.style.height = "100%";
elemRect.style.opacity = "100";
renderUIObject(elemRect);
$('.elemContainer').draggableParent();
$('.elemContainer').resizableParent();
makeDeselectable();
handleDblClick();
}
var createDefaultElement = function() {
..
..
};
var handleDblClick = function() {
..
..
};
var renderUIObject = function(object) {
..
..
};
var makeDeselectable = function() {
..
..
};
I could clone the element when the browser detects a keydown event
$(window).keydown(function(e) {
if (e.keyCode == 77) {
$('.ui-selected').clone();
return false;
}
});
then append it to #canvas. But the problem is, none of the functions I mentioned above get called with this method.
How can I copy/paste an element (by pressing CMD+C then CMD+V) and call those above functions on the cloned element?
The jQuery.clone method returns the cloned node. So you could adjust your code to do something like this:
var myNodes = $('.ui-selected').clone();
myNodes.each(function () {
createDefaultElement(this);
appendResizeHandles(this);
appendOutline(this);
});
I'm new to js-development. I have the following code:
<html>
<body>
<div><span id="inline">Click here to start editing</span></div>
<script>
var inline = document.getElementById("inline");
inline.onclick = function() {
if (!inline.editable) {
var text = inline.innerText;
inline.innerHTML = "<input type='text' id='inline-editable'>";
inline.editable = true;
var inline_editable = document.getElementById("inline-editable");
inline_editable.value = text;
inline_editable.onblur = function() {
var value = inline_editable.value;
inline.editable = false;
inline.innerHTML = value;
}
inline_editable.onkeypress = function(event) {
if (event.keyCode == 13) {
inline_editable.onblur();
}
}
}
}
</script>
</body>
</html>
Which shows some text inside span and allows inline editing. When I finish editing within just onblur event it work perfectly fine. But if I want to terminate editing by Enter and use the same hander I get an error NotFoundError: DOM Exception 8 in this line:
inline.innerHTML = value;
Nevertheless everything works as I expect. Can anyone help me to avoid this error?
I assume that is happened because I destroy inline-editable element while event handling is not finished and it wants to invoke onchange maybe. Should I have 2 controls all the time an switch their visibility instead?
Problem here is the onblur is triggered twice, the second time, the element is not there which causes the problem. Kill the events
var inline = document.getElementById("inline");
inline.onclick = function() {
if (!inline.editable) {
var text = inline.innerText;
inline.innerHTML = "<input type='text' id='inline-editable'>";
inline.editable = true;
var inline_editable = document.getElementById("inline-editable");
inline_editable.value = text;
inline_editable.onblur = function() {
this.onblur = function(){};
var value = this.value;
inline.editable = false;
inline.innerHTML = value;
}
inline_editable.onkeypress = function(event) {
if (event.keyCode == 13) {
this.onblur();
}
}
}
}