I've tried to simplify the code as much as possible. What I'm trying to do is implement a way to resize some elements. To do so, I create a <div class="resize-on"> (it's the orange colored one) and then if you click and hold your mouse down and move then some action should happen like so
var Ui = {};
Ui.Resizer = function (element) {
this.isResizing = false;
this.element = element;
this.resizer = document.createElement("div");
this.resizer.classList.add('resize-on');
var toWrap = this.element;
var parent = toWrap.parentNode;
var next = toWrap.nextSibling;
this.resizer.appendChild(toWrap);
parent.insertBefore(this.resizer, next);
this.resizer.onmousedown = function(event) {
this.isResizing = true;
console.log('onmousedown', this.isResizing);
}.bind(this);
document.querySelector("body").onmousemove = function(event) {
if(this.isResizing) {
console.log('onmousemove', this.isResizing);
}
}.bind(this);
document.querySelector("body").onmouseup = function(event) {
this.isResizing = false;
console.log('mouse up');
}.bind(this);
};
(function() {
Array.prototype.forEach.call(
document.querySelectorAll('.resizable'),
function (element) {
new Ui.Resizer(
element
);
}
);
})();
.resize-on {
background-color: orange;
}
<div class="wrap">
<div class="one">One</div>
<div class="two resizable">two</div>
<div class="three">three</div>
<div class="four">four</div>
<div class="five">five</div>
</div>
So this works fine.
Now the problem is if I have multiple resizable elements it does not work. It then only works on the "last" one (in this case <div class="four resizable">four</div>) Why is that?
The element <div class="two resizable">two</div> get's the mousedown but does not show the onmousemove anymore.
It's the exact same code as above. Just added the resizable class to another HTML element. I can't understand why this doesn't work. Can anyone shed some light on this? Also what do I have to change to get it to work?
var Ui = {};
Ui.Resizer = function (element) {
this.isResizing = false;
this.element = element;
this.resizer = document.createElement("div");
this.resizer.classList.add('resize-on');
var toWrap = this.element;
var parent = toWrap.parentNode;
var next = toWrap.nextSibling;
this.resizer.appendChild(toWrap);
parent.insertBefore(this.resizer, next);
this.resizer.onmousedown = function(event) {
this.isResizing = true;
console.log('onmousedown', this.isResizing);
}.bind(this);
document.querySelector("body").onmousemove = function(event) {
if(this.isResizing) {
console.log('onmousemove', this.isResizing);
}
}.bind(this);
document.querySelector("body").onmouseup = function(event) {
this.isResizing = false;
console.log('mouse up');
}.bind(this);
};
(function() {
Array.prototype.forEach.call(
document.querySelectorAll('.resizable'),
function (element) {
new Ui.Resizer(
element
);
}
);
})();
.resize-on {
background-color: orange;
}
<div class="wrap">
<div class="one">One</div>
<div class="two resizable">two</div>
<div class="three">three</div>
<div class="four resizable">four</div>
<div class="five">five</div>
</div>
Use addEventListener for the events. Using the property onmousemove and others on the body element is only going to allow you to add a single event listener, as the previous one is going to be overwritten, hence why only the last one done, in this case the one for "four", works.
document.querySelector("body").addEventListener("mousemove", function(event) {
this.isResizing = false;
console.log('mouse up');
}.bind(this);
Though you can make it so you only need to set a single listener with only a little modification.
Ui.Resizer = function(element){
//...other code
this.resizer.onmousedown = function(event) {
//Set a property on Ui for use to know which Resizer is active and
//access it in the body events.
Ui.active = this;
}.bind(this);
//...rest of code excluding body events (mousemove, mouseup)
};
document.body.addEventListener("mousemove",function(event){
if(Ui.active){
//if Ui.active is set, active will be the Resizer instance
//and therefore can access the element it was created for
//by using active.element
console.log("mousemove",Ui.active.element);
}
});
document.body.addEventListener("mouseup",function(event){
if(Ui.active){
console.log("mouseup",Ui.active.element);
//clear active
Ui.active = null;
}
});
Demo
var Ui = {};
Ui.Resizer = function(element) {
this.isResizing = false;
this.element = element;
this.resizer = document.createElement("div");
this.resizer.classList.add('resize-on');
var toWrap = this.element;
var parent = toWrap.parentNode;
var next = toWrap.nextSibling;
this.resizer.appendChild(toWrap);
parent.insertBefore(this.resizer, next);
this.resizer.onmousedown = function(event) {
//Set a property on Ui for use to know which Resizer is active and
//access it in the body events.
Ui.active = this;
}.bind(this);
};
document.body.addEventListener("mousemove", function(event) {
if (Ui.active) {
//if Ui.active is set, active will be the Resizer instance
//and therefore can access the element it was created for
//by using active.element
console.log("mousemove", Ui.active.element);
}
});
document.body.addEventListener("mouseup", function(event) {
if (Ui.active) {
console.log("mouseup", Ui.active.element);
//clear active
Ui.active = null;
}
});
(function() {
Array.prototype.forEach.call(
document.querySelectorAll('.resizable'),
function (element) {
new Ui.Resizer(
element
);
}
);
})();
.resize-on {
background-color: orange;
}
<div class="wrap">
<div class="one">One</div>
<div class="two resizable">two</div>
<div class="three">three</div>
<div class="four resizable">four</div>
<div class="five">five</div>
</div>
Related
I can used window.getSelection to get selected words.
<div id="content">
<p class="todo">If you don't know, you can google</p>
</div>
<script>
let selectionObj = null;
let selectedText = "";
function getCurrentSelect() {
if (window.getSelection) {
selectionObj = window.getSelection();
selectedText = selectionObj.toString();
return {
selectText: selectedText
}
}
}
window.onload = function (){
document.body.addEventListener('mouseup', function(){
// if (selectOb) console.log(getCurrentSelect()===selectOb);
// selectOb = getCurrentSelect();
console.log('onmouseup');
console.log(getCurrentSelect());
})
}
</script>
if have two or more identical elements.
<div id="content">
<p class="todo">If you don't know, you can google</p>
<p class="todo">If you don't know, you can google</p>
<p class="todo">If you don't know, you can google</p>
</div>
how to know which element selected?
I want to be able to return an element object when selecting text.But I don't know what to do.
window.getSelection() will return a selection object which contains some of the properties that you might be interested in. For example, you can access the parent element of the text you select via window.getSelection().baseNode.parentElement. This will return a parent element which you could manipulate with your code furthermore.
In the code below, I will get the parent node of the selected text, and change the color of it (style property) to red. Please find it here:
let selectionObj = null;
let selectedText = '';
function getCurrentSelect() {
if (window.getSelection) {
selectionObj = window.getSelection();
selectedText = selectionObj.toString();
return {
selectText: selectedText,
parentElement: selectionObj.baseNode.parentElement,
};
}
}
window.onload = function () {
document.body.addEventListener('mouseup', function () {
console.log('onmouseup');
const { parentElement } = getCurrentSelect(); // this one will give the parent element of the selected text
parentElement.style.color = 'red'; // we change one of the properties of the parent element
console.log(getCurrentSelect());
});
};
let selectionObj = null;
let selectedText = "";
window.onload = function (){
document.body.addEventListener('mouseup', function(e){
// if (selectOb) console.log(getCurrentSelect()===selectOb);
// selectOb = getCurrentSelect();
console.log('onmouseup');
console.log(e.target.innerText);
})
}
<div id="content">
<p class="todo">If you don't know, you can google</p>
</div>
I have button that creates a div on click. I want to return this created div when I click a button. But the following code actually returns this button.
var create = $('#create').on('click', function(e){
var content = $('<div class="foo"/>')
return content
})
var test = create.trigger('click')
console.log(test)
Result is:
init [div#create, context: document, selector: '#create']
Is this not possible to do this this way or am I missing something?
No, it is not possible. You can add a function which will be executed in your event handler to do something with the object you create in the listener:
var create = $('#create').on('click', function(e){
var content = $('<div class="foo"/>')
doSomething(content)
})
create.trigger('click')
function doSomething(test) {
console.log(test)
}
There is no other way and it is because the handler function assigned with .on() method is called when the browser triggers an event (or you use .trigger() method) and the return statement is used only to force calling event.stopPropagation() and event.preventDefault() methods (you have to return false in the handler or just assign false instead of a function as an event handler - check the documentation, section The event handler and its environment) and not to return any value when you trigger an event manually.
You can also use an external variable to store the data "generated" in your event handler:
const divs = []
var create = $('#create').on('click', function(e){
var content = $('<div class="foo"/>')
divs.push(content)
doSomething()
})
create.trigger('click')
function doSomething() {
console.dir(divs)
}
You're calling a variable ("create") which stores the event listener on the button. This is what it looks like:
var test = $('#create').on('click', function(e){
var content = $('<div class="foo"/>')
return content
}).trigger('click')
console.log(test)
This is the solution:
jQuery
var create = function() {
return $('<div class="foo"/>');
};
var createEl = $('#create');
createEl.on('click', function() {
console.log(create());
// <div class="foo"></div>
});
createEl.trigger("click");
JavaScript
var create = function() {
var el = document.createElement('div');
el.className = "foo";
// Add other attributes if you'd like
return el;
};
var createEl = document.querySelector('#create');
createEl.addEventListener("click", function() {
console.log(create());
// <div class="foo"></div>
});
createEl.click();
(jQuery) Live example
var create = function() {
return $('<div class="foo"/>');
};
var createEl = $('#create');
createEl.on('click', function() {
console.log(create());
// <div class="foo"></div>
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<button id="create">Create</button>
(JavaScript) Live example
var create = function() {
var el = document.createElement('div');
el.className = "foo";
// Add other attributes if you'd like
return el;
};
var createEl = document.querySelector('#create');
createEl.addEventListener("click", function() {
console.log(create());
// <div class="foo"></div>
});
createEl.trigger("click");
var create = function() {
var el = document.createElement('div');
el.className = "foo";
// Add other attributes if you'd like
return el;
};
var createEl = document.querySelector('#create');
createEl.addEventListener("click", function() {
console.log(create());
// <div class="foo"></div>
});
<button id="create">Create</button>
I want to make a common function that can be call in html so I can update the value from variable.
function getActivityPoint(activityName) {
document.getElementsByClassName('share_points')[0].innerHTML = activityName;
}
my variable
var ap = {
'movie_review':'20',
}
And in HTML I would like to do this.
<div class="share_points" onload="getActivityPoint(ap.movie_review)">-</div>
<div class="share_points" onload="getActivityPoint(ap.game_review)">-</div>
.............
The inline onload event does not work for a div.
You may change your code in order to change your divs, using data attributes, like:
<div class="share_points" data-load="movie_review">-</div>
and on window load you can do your work:
window.addEventListener("load", function(event) {
document.querySelectorAll('.share_points').forEach(ele => getActivityPoint(ele, ap[ele.dataset.load]));
});
function getActivityPoint(ele, activityName) {
ele.textContent = activityName;
}
var ap = {
'movie_review':'20',
'game_review': 10
}
/****************
For Compatibility issue you may write:
window.addEventListener("load", function (event) {
Array.prototype.slice.call(document.querySelectorAll('.share_points')).forEach(function (ele) {
getActivityPoint(ele, ap[ele.dataset.load]);
});
});
For IE < 9 the solution is:
window.addEventListener("load", function (event) {
var divs = document.querySelectorAll('.share_points');
for (i = 0; i < divs.length; i++) {
getActivityPoint(divs[i], ap[divs[i].dataset.load]);
}
});
*/
<div class="share_points" data-load="movie_review">-</div>
<div class="share_points" data-load="game_review">-</div>
You can do:
var ap = {
'movie_review': '20',
'game_review': '100'
},
share_points = document.getElementsByClassName('share_points');
window.addEventListener('load', function() {
Array.prototype.forEach.call(share_points, function(el) {
el.innerHTML = ap[el.dataset.review];
});
});
<div class="share_points" data-review="movie_review">-</div>
<div class="share_points" data-review="game_review">-</div>
If I click on the first "Edit" I get a console.log('click happend') But if I add a one of these boxes via javascript (click on "Add box") and then the Edit click from this new box does not work. I know it's because the javascript run when the element was not there and that's why there is no click event listener. I also know with jQuery I could do like so:
$('body').on('click', '.edit', function(){ // do whatever };
and that would work.
But how can I do this with plain Javascript? I couldn't find any helpful resource. Created a simple example which I would like to be working. What is the best way to solve this?
So the problem is: If you add a box and then click on "Edit" nothing happens.
var XXX = {};
XXX.ClickMe = function(element){
this.element = element;
onClick = function() {
console.log('click happend');
};
this.element.addEventListener('click', onClick.bind(this));
};
[...document.querySelectorAll('.edit')].forEach(
function (element, index) {
new XXX.ClickMe(element);
}
);
XXX.PrototypeTemplate = function(element) {
this.element = element;
var tmpl = this.element.getAttribute('data-prototype');
addBox = function() {
this.element.insertAdjacentHTML('beforebegin', tmpl);
};
this.element.addEventListener('click', addBox.bind(this));
};
[...document.querySelectorAll('[data-prototype]')].forEach(
function (element, index) {
new XXX.PrototypeTemplate(element);
}
);
[data-prototype] {
cursor: pointer;
}
<div class="box"><a class="edit" href="#">Edit</a></div>
<span data-prototype="<div class="box"><a class="edit" href="#">Edit</a></div>">Add box</span>
JSFiddle here
This Q/A is useful information but it does not answer my question on how to solve the problem. Like how can I invoke the eventListener(s) like new XXX.ClickMe(element); for those elements inserted dynamically into DOM?
Here's a method that mimics $('body').on('click', '.edit', function () { ... }):
document.body.addEventListener('click', function (event) {
if (event.target.classList.contains('edit')) {
...
}
})
Working that into your example (which I'll modify a little):
var XXX = {
refs: new WeakMap(),
ClickMe: class {
static get (element) {
// if no instance created
if (!XXX.refs.has(element)) {
console.log('created instance')
// create instance
XXX.refs.set(element, new XXX.ClickMe(element))
} else {
console.log('using cached instance')
}
// return weakly referenced instance
return XXX.refs.get(element)
}
constructor (element) {
this.element = element
}
onClick (event) {
console.log('click happened')
}
},
PrototypeTemplate: class {
constructor (element) {
this.element = element
var templateSelector = this.element.getAttribute('data-template')
var templateElement = document.querySelector(templateSelector)
// use .content.clone() to access copy fragment inside of <template>
// using template API properly, but .innerHTML would be more compatible
this.template = templateElement.innerHTML
this.element.addEventListener('click', this.addBox.bind(this))
}
addBox () {
this.element.insertAdjacentHTML('beforeBegin', this.template, this.element)
}
}
}
Array.from(document.querySelectorAll('[data-template]')).forEach(function (element) {
// just insert the first one here
new XXX.PrototypeTemplate(element).addBox()
})
// event delegation instead of individual ClickMe() event listeners
document.body.addEventListener('click', function (event) {
if (event.target.classList.contains('edit')) {
console.log('delegated click')
// get ClickMe() instance for element, and create one if necessary
// then call its onClick() method using delegation
XXX.ClickMe.get(event.target).onClick(event)
}
})
[data-template] {
cursor: pointer;
}
/* compatibility */
template {
display: none;
}
<span data-template="#box-template">Add box</span>
<template id="box-template">
<div class="box">
<a class="edit" href="#">Edit</a>
</div>
</template>
This uses WeakMap() to hold weak references to each instance of ClickMe(), which allows the event delegation to efficiently delegate by only initializing one instance for each .edit element, and then referencing the already-created instance on future delegated clicks through the static method ClickMe.get(element).
The weak references allow instances of ClickMe() to be garbage collected if its element key is ever removed from the DOM and falls out-of-scope.
You can do something like this...
document.addEventListener('click',function(e){
if(e.target && e.target.className.split(" ")[0]== 'edit'){
new XXX.ClickMe(e.target);}
})
var XXX = {};
XXX.ClickMe = function(element) {
this.element = element;
this.element.addEventListener('click', onClick.bind(this));
};
XXX.PrototypeTemplate = function(element) {
this.element = element;
var tmpl = this.element.getAttribute('data-prototype');
addBox = function() {
this.element.insertAdjacentHTML('beforebegin', tmpl);
};
this.element.addEventListener('click', addBox.bind(this));
};
[...document.querySelectorAll('[data-prototype]')].forEach(
function(element, index) {
new XXX.PrototypeTemplate(element);
}
);
document.addEventListener('click', function(e) {
if (e.target && e.target.className.split(" ")[0] == 'edit') {
console.log('click happend');
}
})
[data-prototype] {
cursor: pointer;
}
<div class="box"><a class="edit" href="#">Edit</a></div>
<span data-prototype="<div class="box"><a class="edit" href="#">Edit</a></div>">Add box</span>
Do it like jQuery: have a parent element that controls the event delegation. In the following, I use document.body as the parent:
document.body.addEventListener('click', e => {
if (e.target.matches('.edit')) {
// do whatever
}
});
Working example:
var XXX = {};
XXX.PrototypeTemplate = function(element) {
this.element = element;
var tmpl = this.element.getAttribute('data-prototype');
addBox = function() {
this.element.insertAdjacentHTML('beforebegin', tmpl);
};
this.element.addEventListener('click', addBox.bind(this));
};
new XXX.PrototypeTemplate(document.querySelector('[data-prototype]'));
document.body.addEventListener('click', e => {
if (e.target.matches('.edit')) {
// do whatever
console.log('click happend');
}
});
[data-prototype] {
cursor: pointer;
}
<div class="box"><a class="edit" href="#">Edit</a></div>
<span data-prototype="<div class="box"><a class="edit" href="#">Edit</a></div>">Add box</span>
Take a look at what MDN says about Element.prototype.matches.
Thanks to all answering on the question, it is all helpfull information. What about the following:
Wrap all the functions needed inside a XXX.InitializeAllFunctions = function(wrap) {} and pass the document as the wrap on first page load. So it behaves like it did before. When inserting new DOM Elements just pass those also to this function before inserting into DOM. Works like a charm:
var XXX = {};
XXX.ClickMe = function(element){
this.element = element;
onClick = function() {
console.log('click happend');
};
this.element.addEventListener('click', onClick.bind(this));
};
XXX.PrototypeTemplate = function(element) {
this.element = element;
addBox = function() {
var tmpl = this.element.getAttribute('data-prototype');
var html = new DOMParser().parseFromString(tmpl, 'text/html');
XXX.InitializeAllFunctions(html); // Initialize here on all new HTML
// before inserting into DOM
this.element.parentNode.insertBefore(
html.body.childNodes[0],
this.element
);
};
this.element.addEventListener('click', addBox.bind(this));
};
XXX.InitializeAllFunctions = function(wrap) {
var wrap = wrap == null ? document : wrap;
[...wrap.querySelectorAll('[data-prototype]')].forEach(
function (element, index) {
new XXX.PrototypeTemplate(element);
}
);
[...wrap.querySelectorAll('.edit')].forEach(
function (element, index) {
new XXX.ClickMe(element);
}
);
};
XXX.InitializeAllFunctions(document);
[data-prototype] {
cursor: pointer;
}
<div class="box"><a class="edit" href="#">Edit</a></div>
<span data-prototype="<div class="box"><a class="edit" href="#">Edit</a></div>">Add box</span>
i want to build a div with scroll, that when you scroll this div, it will active anothe function.
i need to build this in a Object.
there is any way to do this?
i write here an example source (that not work) of what i want.
<script type="text/javascript">
function onsc(divName, divChange) {
this.play = function() {
window.onload = function() {
document.getElementById(divName).onscroll = function() {
this.scroll(n)
}
};
}
this.scroll = function(n) {
document.getElementById(divChange).innerHTML = "you scroll!";
}
}
c[1] = new onsc("div1", "div1_i").play();
</script>
<div id="div1_i">this div will change when you scroll</div>
<div id="div1" style="background:#C6E2FF; width:300px; height:200px; overflow-y:scroll;">
<p style="height:800px;">txt</p>
</div>
Your code was nearly there. I made a few changes and put into a JSFiddle for you.
I added comments at what you missed. Most importantly the context of this changes when you entered into that function on the onscroll event.
JavaScript
function onsc(divName, divChange) {
// First of all make `this` inherit to below functions
var self = this;
this.play = function () {
document.getElementById(divName).onscroll = function() {
// Changed this to call the parent and place the correct DIV
self.scroll(divChange)
}
}
this.scroll = function (n) {
document.getElementById(divChange).innerHTML = "you scroll!";
}
}
c = new onsc("div1", "div1_i").play();
Demo
Have a look at my JSFiddle: http://jsfiddle.net/bJD8w/2/