I have just two elements, a div with an object attached as child, which contains an SVG image. The click handler works fine when there is just the div by itself, or another tag as it's child, such as img. However, when the object is a child, the div click handler is only triggered in the very bottom right corner. I'm really stuck as to what would be causing this behavior.
let divContainer = document.createElement('div');
divContainer.className = 'divContainer';
divContainer.style.position = 'absolute'
divContainer.style.top = '100px';
divContainer.style.width = '150px';
divContainer.style.height = '150px';
divContainer.style.background = 'green';
let imageChild = document.createElement('object');
imageChild.type = 'image/svg+xml';
imageChild.data = 'https://svgsilh.com/svg_v2/48363.svg'
imageChild.className = 'imageChild';
imageChild.style.width = 'inherit';
document.body.append(divContainer);
// Comment this out, and it works fine
divContainer.append(imageChild);
divContainer.onclick = function(event) {
console.log('Click OK!');
}
here is the working code
let divContainer = document.createElement('div');
divContainer.className = 'divContainer';
divContainer.style.top = '100px';
divContainer.style.width = '150px';
divContainer.style.height = '150px';
divContainer.style.background = 'green';
let imageChild = document.createElement('object');
imageChild.type = 'image/svg+xml';
imageChild.data = 'https://svgsilh.com/svg_v2/48363.svg';
imageChild.className = 'imageChild';
imageChild.style.width = 'inherit';
imageChild.style.pointerEvents = 'none';
document.body.append(divContainer);
// Comment this out, and it works fine
divContainer.append(imageChild);
divContainer.addEventListener('click', function () {
alert('Click OK!');
});
Related
How to MutationObserver for new created element? When I create a new element, mutatonObserver does not work. I need to register if the giant box has changed style ...
<button onclick="myFunction()">Create</button>
<button onclick="myFunctionStyle()">Change style</button>
var observerm = new MutationObserver(function(mutationsm) {
mutationsm.forEach(function(mutationRecord) {
alert('style changed!');
});
});
var targetm = document.getElementById("Box");
observerm.observe(targetm, { attributes : true, attributeFilter : ['style'] });
function myFunction() {
var newdiv = document.createElement("DIV");
newdiv.innerHTML = "Box";
newdiv.id = "Box";
newdiv.style.width = "100px";
newdiv.style.height = "50px";
newdiv.style.backgroundColor = "red";
document.body.appendChild(newdiv);
}
function myFunctionStyle() {
document.getElementById("Box").style.backgroundColor = "blue";
}
Since the newly added elements are appended to the document.body, that's the element you need to attach another MutationObserver to.
new MutationObserver(() => {
console.log('mutation on document body');
// rest of the code you need when an element is appended
})
.observe(document.body, { childList: true })
I'm new with js classes and I think I'm not doing it right.
I want to achieve a simple thing here. On one card click I want to hide all other cards. But how can I reach other cards if event is triggered from inside of one of the cards?
class Cards {
constructor(args){
this.list = [];
this.amm = args.amm;
this.createCards();
}
createCards(){
for(var i=0; i<this.amm; i++){
this.list.push( new Card( {id: i} ) );
}
}
}
class Card {
constructor(args){
this.id = args.id;
this.el = null;
this.createCard();
this.addEvents();
}
createCard(){
this.el = document.createElement("div");
this.el.style.width = "60px";
this.el.style.height = "100px";
this.el.style.backgroundColor = "red";
this.el.style.margin = "5px";
this.el.style.float = "left";
document.body.appendChild(this.el);
}
addEvents(){
let _this = this;
this.el.onclick = function(){
_this.el.style.opacity = 0.7;
_this.hideOtherCards(_this.id);
};
}
hideOtherCards(id){
// how to hide other cards?
}
}
var myCards = new Cards({amm: 5});
It's good practice (encapsulation) to keep the scope of any component limited to itself. That is, a card shouldn't know that, or how many, other cards exist. To keep the cards decoupled, a common way to achieve that is to make use of custom events.
Imagine it like this: A card that is clicked shouts into the room "I was clicked" and relies upon someone hearing that and reacting to that and for that instance to know what to do. If noone reacts, your code still won't throw an error.
For this to work in your scenario, you'd need a host element for the cards as events bubble up the DOM, but don't bubble to siblings.
Long story short, this is what I'd do:
Edit: Actually, the myCards class should be responsible for creating the host element, and listening to card-clicked.
class Cards {
constructor(args){
this.list = [];
this.el = null;
this.amm = args.amm;
this.createCardHost();
}
createCardHost() {
this.el = document.createElement('div');
this.createCards();
this.el.addEventListener('card-clicked', (e) => {
this.list.forEach(card => {card.id === e.detail.id ? card.el.style.opacity = 0.7 : card.el.style.opacity = 0.1})
})
for (const card of this.list) {
this.el.appendChild(card.el)
}
document.body.appendChild(this.el);
}
createCards(){
for(var i=0; i<this.amm; i++){
this.list.push( new Card( {id: i} ) );
}
}
}
class Card {
constructor(args){
this.id = args.id;
this.el = null;
this.createCard();
this.addEvents();
}
createCard(){
this.el = document.createElement("div");
this.el.style.width = "60px";
this.el.style.height = "100px";
this.el.style.backgroundColor = "red";
this.el.style.margin = "5px";
this.el.style.float = "left";
}
addEvents(){
this.el.addEventListener('click', () => {
this.el.style.opacity = 0.7;
// throw a 'card-clicked' event here
const cardClicked = new CustomEvent('card-clicked', { bubbles: true, cancelable: true, detail: { id: this.id }});
this.el.dispatchEvent(cardClicked);
});
}
}
var myCards = new Cards({amm: 5});
I followed this control-button-leaflet tutorial and it worked for me. Now I want to:
show some text when i hover over the button (like with the zoom buttons)
Change the color of the button when i hover over it
be able to write text inside the button instead of an image.
Here's the code:
var customControl = L.Control.extend({
options: {
position: 'topleft'
},
onAdd: function (map) {
var container = L.DomUtil.create('div', 'leaflet-bar leaflet-control leaflet-control-custom');
container.style.backgroundColor = 'white';
container.style.backgroundImage = "url(http://t1.gstatic.com/images?q=tbn:ANd9GcR6FCUMW5bPn8C4PbKak2BJQQsmC-K9-mbYBeFZm1ZM2w2GRy40Ew)";
container.style.backgroundSize = "30px 30px";
container.style.width = '30px';
container.style.height = '30px';
container.onclick = function(){
console.log('buttonClicked');
}
return container;
}
});
var map;
var readyState = function(e){
map = new L.Map('map').setView([48.935, 18.14], 14);
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
map.addControl(new customControl());
}
window.addEventListener('DOMContentLoaded', readyState);
It seems you more need a Button than a div:
var container = L.DomUtil.create('input');
container.type="button";
Then you can easily set a mouseover text:
container.title="No cat";
And some Text instead of an image:
container.value = "42";
And you can use the mouse events to style the button:
container.onmouseover = function(){
container.style.backgroundColor = 'pink';
}
container.onmouseout = function(){
container.style.backgroundColor = 'white';
}
(you could of course do this last part with css, might be more elegant)
Full example: http://codepen.io/anon/pen/oXVMvy
I have a class, that is supposed to display grey overlay over page and display text and loading gif. Code looks like this:
function LoadingIcon(imgSrc) {
var elem_loader = document.createElement('div'),
elem_messageSpan = document.createElement('span'),
loaderVisible = false;
elem_loader.style.position = 'absolute';
elem_loader.style.left = '0';
elem_loader.style.top = '0';
elem_loader.style.width = '100%';
elem_loader.style.height = '100%';
elem_loader.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
elem_loader.style.textAlign = 'center';
elem_loader.appendChild(elem_messageSpan);
elem_loader.innerHTML += '<br><img src="' + imgSrc + '">';
elem_messageSpan.style.backgroundColor = '#f00';
this.start = function () {
document.body.appendChild(elem_loader);
loaderVisible = true;
};
this.stop = function() {
if (loaderVisible) {
document.body.removeChild(elem_loader);
loaderVisible = false;
}
};
this.setText = function(text) {
elem_messageSpan.innerHTML = text;
};
this.getElems = function() {
return [elem_loader, elem_messageSpan];
};
}
Problem is, when I use setText method, it sets innerHTML of elem_messageSpan, but it doesn't reflect to the span, that was appended to elem_loader. You can use getElems method to find out what both elements contains.
Where is the problem? Because I don't see single reason why this shouldn't work.
EDIT:
I call it like this:
var xml = new CwgParser('cwg.geog.cz/cwg.xml'),
loader = new LoadingIcon('./ajax-loader.gif');
xml.ondataparse = function() {
loader.stop();
document.getElementById('cover').style.display = 'block';
};
loader.setText('Loading CWG list...');
loader.start();
xml.loadXML();
xml.loadXML() is function that usually takes 3 - 8 seconds to execute (based on download speed of client), thats why I'm displaying loading icon.
I have created a custom close button for the InfoBox, but I cannot get it to close. I have tried to create an eventlistener for when the box is clicked, but no luck so far. The code is as follows:
InfoPopupbox.prototype = new google.maps.OverlayView();
InfoPopupbox.prototype.onAdd = function() {
//add is after the map has been initiated
var div = document.createElement('div');
var classname = "infoWrapper ";
if (this.customclass){
classname += this.customclass;
}
div.className = classname ;
div.style.border = "none";
div.style.borderWidth = "0px";
div.style.position = "absolute";
div.appendChild(this.content_);
this.div_ = div;
var panes = this.getPanes();
//panes.overlayLayer.appendChild(div);
//float the Pane above the map
panes.floatPane.appendChild(div);
google.maps.event.addListener( this , 'click' , function(){
console.log('click close');
that.infotoggle();
});
};
You want to use addDomListener instead of addListener for click, and you'll need to bind it to the DOM Element:
google.maps.event.addDomListener(this.div, 'click', function() { ... });