I'm sure this has been asked before, but I haven't had any luck finding an answer. There's probably a term for this which I don't know.
Can a jQuery event handler return an element other than the one from which it was triggered?
Normally, when you trigger a jQuery event for an element, that element will be returned by the event handler. Is there a way to return a different element instead? (other than event.target, event.currentTarget, etc.)
Here's my HTML:
<div id="content">
<div id="wrapper">
<div class="instance">
</div>
</div>
</div>
If I make a custom jQuery event for #wrapper, can I make its event handler return an .instance?
Here's my JavaScript:
$('#content').on('new.instance', '#wrapper', function(event) {
var wrapper = $(event.currentTarget);
var instance = wrapper.find('div.instance').first();
var newInstance = instance.clone(true, true);
newInstance.appendTo(wrapper);
return newInstance;
});
var returnValue = $('#wrapper').trigger('new.instance');
console.log('returnValue.html():', returnValue.html()); // #wrapper, not .instance
Can I make the new.instance event handler return an .instance element? If so, what am I missing in the code?
.trigger()
...
When we define a custom event type using the .on() method, the second argument to .trigger() can become useful.
You could simply add an object to the .trigger method as a second parameter and collect your instances in there.
var instances = {data:[]};
$('#content').on('new.instance', '#wrapper', function(event, instances) {
var wrapper = $(event.currentTarget);
var instance = wrapper.find('div.instance').first();
var newInstance = instance.clone(true, true);
instances.data.push(newInstance);
newInstance.appendTo(wrapper);
});
$('#wrapper').trigger('new.instance', instances);
console.log(instances.data[0].get(0));
// somewhere else at a latter time trigger new.instance again
setTimeout(function() {
$('#wrapper').trigger('new.instance', instances);
$.each(instances.data, function(index, item) {
var counter = index + 1
item.text( 'cloned instance ' + counter );
console.log(item.get(0))
})
}, 2000)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="content">
<div id="wrapper">
<div class="instance"></div>
</div>
</div>
.instance is in the event.chain so you can access it on callback during the bubbling phase. Review the Snippet by clicking each element.
Snippet
var wrp = document.getElementById('wrapper');
wrp.addEventListener('click', function(e) {
if (e.target != e.currentTarget) {
var trueTarget = e.target.className;
alert(trueTarget + ' has been clicked!');
}
e.stopPropagation();
}, false);
#content {
border: 2px dashed grey;
width: 50vw;
height: 50vh;
background: rgba(0, 0, 0, .3);
text-align: right;
color: white;
}
#wrapper {
border: 3px dotted orange;
background: rgba(0, 0, 0, .5);
width: 50%;
height: 50%;
text-align: right;
font-size: small;
color: orange;
}
.instance {
border: 1px solid yellow;
width: 50%;
height: 50%;
background: rgba(0, 0, 0, .7);
text-align: center;
font-size: smaller;
color: yellow;
}
<div id="content">
CONTENT
<div id="wrapper">
WRAPPER
<div class="instance">
INSTANCE
</div>
</div>
</div>
Related
I'm wondering if it's possible to on each appendTo make the new div unique but still use the same jquery.
As you can see in the mark-up below, each new div shares the same jquery so doesn't work independently.
Within my Javascript i'm selecting the ID to fire each function.
I've tried just adding + 1 etc to the end of each ID, but with that it changes the name of the ID making the new created DIV not function.
I've thought of using DataAttribues, but i'd still have the same issue having to create multiple functions all doing the same job.
Any ideas?
Thanks
$(function() {
var test = $('#p_test');
var i = $('#p_test .upl_drop').length + 1;
$('#addtest').on('click', function() {
$('<div class="file-input"><div class="input-file-container upl_drop"><label for="p_test" class="input-file-trigger">Select a file...<input type="file" id="p_test" name="p_test_' + i + '" value=""class="input-file"></label></div><span class="remtest">Remove</span><p class="file-return"></p></div>').appendTo(test);
i++;
});
$('body').on('click', '.remtest', function(e) {
if (i > 2) {
$(this).closest('.file-input').remove();
i--;
}
});
});
var input = document.getElementById( 'file-upload' );
var infoArea = document.getElementById( 'file-upload-filename' );
input.addEventListener( 'change', showFileName );
function showFileName( event ) {
// the change event gives us the input it occurred in
var input = event.srcElement;
// the input has an array of files in the `files` property, each one has a name that you can use. We're just using the name here.
var fileName = input.files[0].name;
// use fileName however fits your app best, i.e. add it into a div
textContent = 'File name: ' + fileName;
$("#input-file-trigger").text(function () {
return $(this).text().replace("Select a file...", textContent);
});
}
/*
#### Drag & Drop Box ####
*/
.p_test{
display: inline-block;
}
.upl_drop{
border: 2px dashed #000;
margin: 0px 0px 15px 0px;
}
.btn--add p{
cursor: pointer;
}
.input-file-container {
position: relative;
width: auto;
}
.input-file-trigger {
display: block;
padding: 14px 45px;
background: #ffffff;
color: #1899cd;
font-size: 1em;
cursor: pointer;
}
.input-file {
position: absolute;
top: 0; left: 0;
width: 225px;
opacity: 0;
padding: 14px 0;
cursor: pointer;
}
.input-file:hover + .input-file-trigger,
.input-file:focus + .input-file-trigger,
.input-file-trigger:hover,
.input-file-trigger:focus {
background: #1899cd;
color: #ffffff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class="p_test" id="p_test">
<div class="file-input">
<div class="input-file-container upl_drop">
<input class="input-file" id="file-upload" type="file">
<label tabindex="0" for="file-upload" id="input-file-trigger" class="input-file-trigger">Select a file...</label>
</div>
<div id="file-upload-filename"></div>
</div>
<button class="btn--add" id="addtest">
Add
</button>
</div>
I'd advise against using incremental id attributes. They become a pain to maintain and also make the logic much more complicated than it needs to be.
The better alternative is to use common classes along with DOM traversal to relate the elements to each other, based on the one which raised any given event.
In your case, you can use closest() to get the parent .file-input container, then find() any element within that by its class. Something like this:
$(function() {
var $test = $('#p_test');
$('#addtest').on('click', function() {
var $lastGroup = $test.find('.file-input:last');
var $clone = $lastGroup.clone();
$clone.find('.input-file-trigger').text('Select a file...');
$clone.insertAfter($lastGroup);
});
$test.on('click', '.remtest', function(e) {
if ($('.file-input').length > 1)
$(this).closest('.file-input').remove();
}).on('change', '.input-file', function(e) {
if (!this.files)
return;
var $container = $(this).closest('.file-input');
$container.find(".input-file-trigger").text('File name: ' + this.files[0].name);
});
});
.p_test {
display: inline-block;
}
.upl_drop {
border: 2px dashed #000;
margin: 0px 0px 15px 0px;
}
.btn--add p {
cursor: pointer;
}
.input-file-container {
position: relative;
width: auto;
}
.input-file-trigger {
display: block;
padding: 14px 45px;
background: #ffffff;
color: #1899cd;
font-size: 1em;
cursor: pointer;
}
.input-file {
position: absolute;
top: 0;
left: 0;
width: 225px;
opacity: 0;
padding: 14px 0;
cursor: pointer;
}
.input-file:hover+.input-file-trigger,
.input-file:focus+.input-file-trigger,
.input-file-trigger:hover,
.input-file-trigger:focus {
background: #1899cd;
color: #ffffff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class="p_test" id="p_test">
<div class="file-input">
<div class="input-file-container upl_drop">
<input class="input-file" type="file">
<label tabindex="0" for="file-upload" class="input-file-trigger">Select a file...</label>
</div>
<div class="file-upload-filename"></div>
</div>
<button class="btn--add" id="addtest">Add</button>
</div>
Note that I've made a couple of other optimisations to the code. Firstly it now makes a clone() of the last available .file-input container when the Add button is clicked. This is preferred over writing the HTML in the JS file as it keeps the two completely separate. For example, if you need to update the UI, you don't need to worry about updating the JS now, as long as the classes remain the same.
Also note that you were originally mixing plain JS and jQuery event handlers. It's best to use one or the other. As you've already included jQuery in the page, I used that as it makes the code easier to write and more succinct.
Finally, note that you didn't need to provide a function to text() as you're completely over-writing the existing value. Just providing the new string is fine.
What I'm doing
let test = custom js code that add Event Listeners to #c1 Elements
Create <script #js> </script>
Append test to #js
Delete <script #js> </script> ((( This is a problem )))
Add Event Listeners to #c2 Elements
Delete .box6 ((( This is a problem )))
Problem 4
Even when I delete the <script #js> Custom Code Appened for #c1 Elements </script>,
#c1 Elements are still clickable… they act as if <script #js> is still there.
Problem 6
Is this truly deleted from memory… or is it similar to Problem 4… meaning it's there… just not seeable. If it's still in memory… How to delete it?
Heads Up
This is a universal problem… not just for Event Listeners, but console.log, alerts,… Once <script #js> is delete… nothing should be happening associated with <script #js> All this should be gone from memory.
Working Demo
let test = `
let container = document.getElementById("c1")
let clicked = container.getElementsByClassName("boxes");
for (let i = 0; i < clicked.length; i++) {
clicked[i].addEventListener('click', b);
}
function b() {
if(this.classList.contains("clicked")) {
this.classList.remove("clicked");
}
else {this.classList.add("clicked");}
}
`;
// Creating Script Tag with #js
// And appending -test- var to it
let script = document.createElement('script');
script.type = 'text/javascript';
script.id = 'js';
script.text = test;
document.body.appendChild(script);
// I deleted #js
// Yet #c1 .boxes event listeners are still attached to divs?
// They are not suppose to be clickable if I deleted #js
// What's going on
let deleteScript = document.getElementById('js');
deleteScript.parentNode.removeChild( deleteScript );
// This code will stay here
let container2 = document.getElementById("c2")
let clicked2 = container2.getElementsByClassName("boxes");
for (let i = 0; i < clicked2.length; i++) {
clicked2[i].addEventListener('click', b2);
}
function b2() {
if(this.classList.contains("clicked2")) {
this.classList.remove("clicked2");
}
else {this.classList.add("clicked2");}
}
// I deleted .box6
// How to remove it properly from DOM… Out of memory
let deleteDiv = document.getElementById('here');
deleteDiv.parentNode.removeChild( deleteDiv );
body {
background: #E7F0F6;
}
.container {
width: calc(100%-20px);
height: 100%;
display: flex;
}
.boxes {
width: 25%;
height: 80px;
background: white;
margin: 10px;
box-shadow: 0px 0px 0px 1px #36BCFF;
border-radius: 15px;
transition: .3s;
text-align: center;
font-size: 60px;
color: #E7F0F6;
line-height: 80px;
}
.boxes:hover {
box-shadow: 0px 0px 0px 1px #36BCFF, 0px 0px 15px rgba(0, 0, 0, 0.25);
}
.clicked {
background: #36BCFF;
color: white;
}
.clicked2 {
background: #637182;
color: white;
}
<div id="c1" class="container">
<div class="boxes box1">1</div>
<div class="boxes box2">2</div>
<div class="boxes box3">3</div>
<div class="boxes box4">4</div>
</div>
<div id="c2" class="container">
<div class="boxes box5">5</div>
<div id="here" class="boxes box6">6</div>
<div class="boxes box7">7</div>
<div class="boxes box8">8</div>
</div>
You can not delete a script tag that caused the browser to evaluate some code and expect it to be gone.
But you can use removeEventListener in order to unregister event listeners,
And you can manipulate variables functions that has been declared using the script tag.
Can't be done... A script tag evaluates as soon as the script tag is rendered. And can't be undone or disabled. You need to recreate the element or remove it's event listener that is attached to a DOM element.
It is erased from memory by the garbage collector no longer can be referensed from somewhere
I am having issue with the mouseenter and the mouseleave event in javascript. The strange thing is that the code works if you substitute these 2 events with click or dblclick events. Hope you can help me here.
PS: I'm using chrome.
don't know how to make js work on fiddle... for now
here's the code:
https://jsfiddle.net/frempong69/t7du0kte/
(function() {
window.onload = function() {
var box = document.getElementsByClassName("box")[0];
var change = function() {
box.style.backgroundColor = "green";
};
var normal = function() {
box.style.backgroundColor = "blue";
}
addEventListener("click", change, false);
addEventListener("mouseleave", normal, false);
};
}());
You are adding the mouseleave/mouseenter handlers to the window object. The click handler works because it bubbles to the window object, but the mouseenter and mouseleave events doesn't bubble so the listeners attached to the window object won't get triggered
You need add the listerns to the box element
(function() {
window.onload = function() {
var box = document.getElementsByClassName("box")[0];
var change = function() {
box.style.backgroundColor = "green";
};
var normal = function() {
box.style.backgroundColor = "blue";
}
box.addEventListener("mouseenter", change, false);
box.addEventListener("mouseleave", normal, false);
};
}());
.box {
background-color: red;
width: 400px;
height: 200px;
margin: 50px auto;
position: relative;
}
.box:after {
content: " ";
width: 0px;
height: 0px;
border-top: 100px solid transparent;
border-right: 100px solid transparent;
border-bottom: 100px solid transparent;
border-left: 100px solid red;
position: absolute;
left: 100%;
top: 50%;
margin-top: -100px
}
<div class="box">
</div>
You can simply do like this
box.onmouseenter = change;
box.mouseleave = normal;
You must change
addEventListener("click", change, false);
addEventListener("mouseleave", normal, false);
with this
box.addEventListener("click", change, false);
box.addEventListener("mouseout", normal, false);
you just use this
<div class="box" onmouseover="style.background='green'" onmouseout="style.background='red'">
</div>
its work
I have a function that alters the size of a div when I click on it. Now I have to write the onclick command in my html page, but I want it to stand in the extern .js file.
Now in html:
<div id="box1" class="kaesten" onclick="changeSize('box1')"> Title 1 </div>
What I want:
<div id="box1" class="kaesten" > Title 1 </div>
Tried something in jquery but it didn't work:
function changeSize(id) {
var elem = document.getElementById(id);
var currentAbsoluteElem = document.getElementById('dummy');
var text = elem.innerHTML;
currentAbsoluteElem.innerHTML = text;
currentAbsoluteElem.setAttribute('style', 'display:block');
/*Extra styling neeed to be done here*/
}
var elems = document.getElementsByClassName('kaesten');
for (var i = 0; i < elems.length; i++) {
elems[i].onclick = function() {
changeSize(this.id);
}
}
var absoluteCl = document.getElementsByClassName('absoluteclass');
absoluteCl[0].onclick = function() {
console.log(document.getElementsByClassName('absoluteclass'))
document.getElementsByClassName('absoluteclass')[0].setAttribute('style', 'display:none');
}
$(document).ready(function() {
$('.kaesten').click(function() {
changeSize($(this).attr('id'));
});
});
.kaesten {
width: 240px;
height: 300px;
background-color: darkgrey;
background-position: center;
background-repeat: no-repeat;
text-shadow: 0px 0px 3px #000;
border: 5px solid #F0F8ff;
vertical-align: top;
text-shadow: 3px 3px 4px #777;
float: left;
margin-left: 30px;
}
.absoluteclass {
position: absolute;
background-color: darkgrey;
width: 600px;
height: 600px;
left: calc(30%);
display: none;
}
<div id="box1" class="kaesten">title1</div>
<div id="box2" class="kaesten">title2</div>
<div id="box3" class="kaesten">title3</div>
<div id="box4" class="kaesten">title4</div>
<div id="dummy" class="absoluteclass"></div>
I know it works in the fiddle, but I don't know why it doesn't work on my homepage without writing the function in the div's.
I guess the problem is that you are trying to assign the onclick event handler before the DOM is actually rendered and ready. My suggestion is to wrap your "initialization code" inside a $(document).ready() method. As follows:
$(document).ready(function() {
// Apply the on click event handlers here, using jQuery or not
// For instance:
$('.kaesten').click(function() {
changeSize($(this).attr('id'));
});
});
if you want to pass the id from jquery to your function you should do it like this:
$(function(){
$(".kaesten").click(function(){
changeSize($(this).attr("id"));
});
});
you can use .css in jquery
$(function(){
$(".kaesten").click(function(){
$(this).css({'width' : '600px' , 'height' : '600px'});;
});
});
I'm building a simple one page app using Polymer. I have created a custom element that contains the Polymer-drag-drop demo. The action of dragging and creating a div works fine, the event object's relatedTarget property holds a reference to the correct drop div. The problem is the srcElement and target property both hold references to the shadowRoot parent polymer element, in this case "workspace-drop".
EDIT:
Logging event.currentTarget on fire also contains a reference to the parentDiv holding the colored children. <div flex horizontal style="border: 1px dotted silver;">
Code is as follows (pretty much the demo but in a polymer element):
<link rel="import" href="/components/polymer/polymer.html">
<script src="/components/webcomponentsjs/webcomponents.js">
</script>
<link rel="import" href="/components/core-drag-drop/core-drag-drop.html">
<polymer-element name="workspace-drop">
<template>
<style>
html {
font-family: 'Helvetica Neue', 'Roboto', 'Arial', sans-serif;
}
body {
height: 100vh;
margin: 0;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
}
.box {
display: inline-block;
width: 100px;
height: 100px;
margin: 16px;
}
.dropped {
position: absolute;
border: 1px solid black;
width: 5px;
height: 5px;
}
</style>
<div flex horizontal style="border: 1px dotted silver;">
<core-drag-drop></core-drag-drop>
<div class="box" style="background-color: lightblue;" draggable="false"></div>
<div class="box" style="background-color: orange;" draggable="false"></div>
<div class="box" style="background-color: lightgreen;" draggable="false"></div>
<div id="hello">Hello World</div>
</div>
<div id="drop" hash="test" class="box" style="border: 3px solid silver; position: relative; width: 300px; height: 300px;" draggable="false"></div>
</template>
<script>
(function(){
addEventListener('drag-start', function(e) {
var dragInfo = e.detail;
// flaw #2: e vs dragInfo.event
console.log(e.detail);
var color = dragInfo.event.target.style.backgroundColor;
dragInfo.avatar.style.cssText = 'border: 3px solid ' + color + '; width: 32px; height: 32px; border-radius: 32px; background-color: whitesmoke';
dragInfo.drag = function() {};
dragInfo.drop = drop;
});
//
function drop(dragInfo) {
var color = dragInfo.avatar.style.borderColor;
var dropTarget = dragInfo.event.relatedTarget;
if (color && dropTarget.id === 'drop') {
var f = dragInfo.framed;
var d = document.createElement('div');
d.className = 'dropped';
d.style.left = f.x - 4 + 'px';
d.style.top = f.y - 4 + 'px';
d.style.backgroundColor = color;
dropTarget.appendChild(d);
dropTarget.style.backgroundColor = color;
}
}
Polymer({
ready: function(){
}
});
})();
</script>
</polymer-element>
Any assistance is appreciated!
Figured it out. It is related to this question.
When loggin an event object, currentTarget is null, but when logging event.currentTarget it logs a value. Why is that?
Logging the event after the drag action has completed returns a reference to the object in its completed state. Logging the specific event.target property on drag-start gave reference to the correct object, on start.