Suppose I want to transform all (existing and dynamically created) <a> tags having a data-params property, e.g. by setting the href attribute.
It seems that this code:
$('body').on('change', 'a[data-params]', function() { ... })
only works on dynamically created elements, not existing elements.
On the other hand, this code:
$('a[data-params]').each(function(index) { ... });
only works on existing elements.
So if I want both (existing and dynamically created), I need both codes, ideally defining my transformation function first, then:
$('a[data-params]').each(function(index) { processDataParams(this); });
$('body').on('change', 'a[data-params]', function() { processDataParams(this); });
or am I missing some simpler way to do this?
$('a[data-params]') returns all nodes with this data attribute. Always.
I think that the problem is before, in the creation of dinamic elements. Avoid use the jQuery data method when you add the elements, because it does not update the DOM (don't adds the desired data-params attribute).
// Add some elements to the current doc
['magenta', 'olive'].forEach(color => {
$('<a>', {html:color})
// .data('params', color) <-- this don't updates de DOM, 👎 jQuery
.attr('data-params', color)
.appendTo('#root')
})
// Element unable to find with $('a[data-params]')
$('<a>', {html: 'This elemnt won\'t update'})
.data('params', 'purple')
.appendTo('#root')
function transform() {
$('a[data-params]').each((i, node) => {
$(node).css('color', $(node).data('params'))
$(node).attr('href', '#' + $(node).data('params'))
})
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div style="display: flex; flex-direction: column" id="root">
<a data-params='red'>red</a>
<a data-params='blue'>blue</a>
<a data-params='green'>green</a>
</div>
<hr>
<button onclick="transform()">Transform Elements</button>
Edited with the corrections of #Spectric and #RokoC.Buljan. Thanks to all.
You can use Jquery Event Delegation to run code (an event listener) on all child (internal) elements of an element, whether or not they already exist (because its set on the parent, which does already exist). You can read more about Event Delegation in JQuery's docs - https://learn.jquery.com/events/event-delegation/
Example:
$('ul').on('click', 'li', function(event) {
//code that will run on al <li> element clicks
});
this code is set on ul element, and allows an event listener to be set for all current and future li elements that are within the ul.
Related
What is the functional difference between these three jQuery methods:
detach()
hide()
remove()
hide() sets the matched elements' CSS display property to none.
remove() removes the matched elements from the DOM completely.
detach() is like remove(), but keeps the stored data and events associated with the matched elements.
To re-insert a detached element into the DOM, simply insert the returned jQuery set from detach():
var span = $('span').detach();
...
span.appendTo('body');
Imagine a piece of paper on a table with some notes written with pencil.
hide -> throw a clothe onto it
empty -> remove the notes with an eraser
detach -> grab the paper in your hand and keep it there for whatever future plans
remove -> grab the paper and throw it to the dustbin
The table represents the current DOM space, the paper represents the element, and the notes represent the contents (child nodes) of the element.
A bit simplified and not completely accurate, but easy to understand.
hide() sets the matched element's display to none.
detach() removes the matched elements, including all text and child nodes.
This method stores all the data associated with the element and so can be used to restore the element's data as well as event handlers.
remove() also removes the matched elements, including all text and child nodes.
However, in this case only the element's data can be restored, not its event handlers can't.
In jQuery, there are three methods for removing elements from the DOM. These three methods are .empty(), .remove(), and .detach(). All these methods are used for removing elements from the DOM, but they all are different.
.hide()
Hide the matched elements. With no parameters, the .hide() method is the simplest way to hide an HTML element:
$(".box").hide();
.empty()
The .empty() method removes all child nodes and content from the selected elements. This method does not remove the element itself, or its attributes.
Note
The .empty() method does not accept any argument to avoid memory leaks. jQuery removes other constructs, such as data and event handlers, from the child elements before removing the elements themselves.
Example
<div class="content">
<div class="hai">Hai</div>
<div class="goodevening">good evening</div>
</div>
<script>
$("div.hai").empty();
</script>
This will result in a DOM structure with the Hai text deleted:
<div class="content">
<div class="hai"></div>
<div class="goodevening">good evening</div>
</div>
If we had any number of nested elements inside <div class="hai">, they would be removed too.
.remove()
The .remove() method removes the selected elements, including all text and child nodes. This method also removes the data and events of the selected elements.
Note
Use .remove() when you want to remove the element itself, as well as everything inside it. In addition to this, all bound events and jQuery data associated with the elements are removed.
EXAMPLE
Consider the following html:
<div class="content">
<div class="hai">Hai</div>
<div class="goodevening">good evening</div>
</div>
<script>
$("div.hai").remove();
</script>
This will result in a DOM structure with the <div> element deleted:
<div class="content">
<div class="goodevening">good evening</div>
</div
If we had any number of nested elements inside <div class="hai">, they would be removed too. Other jQuery constructs, such as data or event handlers, are erased as well.
.detach()
The .detach() method removes the selected elements, including all text and child nodes. However, it keeps data and events. This method also keeps a copy of the removed elements, which allows them to be reinserted at a later time.
Note
The .detach() method is useful when removed elements are to be reinserted into the DOM at a later time.
Example
<!doctype html>
<html>
<head>
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
</head>
<body>
<p>Hai!</p>Good <p>Afternoo</p>
<button>Attach/detach paragraphs</button>
<script>
$( "p" ).click(function() {
$( this ).toggleClass( "off" );
});
var p;
$( "button" ).click(function() {
if ( p ) {
p.appendTo( "body" );
p = null;
} else {
p = $( "p" ).detach();
}
});
</script>
</body>
</html>
For more info, visit: http://www.scriptcafe.in/2014/03/what-is-difference-between-jquery_15.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
</head>
<body>
<script>
$(document).ready(function(){
var $span;
$span = $("<span>");
$span.text("Ngoc Xuan");
function addEvent() {
$span.on("click",function(){
alert("I'm Span");
});
}
function addSpan() {
$span.appendTo("body");
}
function addButton(name) {
var $btn = $("<input>");
$btn.attr({value:name,
type:'submit'});
if(name=="remove"){
$btn.on("click",function(){
$("body").find("span").remove();
})
}else if(name=="detach"){
$btn.on("click",function(){
$("body").find("span").detach();
})
}else if(name=="Add") {
$btn.on("click",function(){
addSpan();
})
}else if(name=="Event"){
$btn.on("click",function(){
addEvent();
})
}else if (name == "Hide") {
$btn.on("click",function(){
if($span.text()!= '')
$span.hide();
})
}else {
$btn.on("click",function(){
$span.show();
})
}
$btn.appendTo("body");
}
(function () {
addButton("remove");
addButton("detach");
addButton("Add");
addButton("Event");
addButton("Hide");
addButton("Show");
})();
});
</script>
</body>
</html>
When adding a dynamically added element, how can I get attributes for that element (when clicking on it, for example)? I figured out that I need to delegate an event, but I still can't access any of the attributes of that event.
This JSFiddle shows the issue problem: https://jsfiddle.net/wgc499n9/
$(this).data('index') comes up as 'undefined' - I think $(this) is referencing 'document' instead of .remove_link; even the event data doesn't seem to have any useful information in it. $(this).attr('id') also comes up as 'undefined'.
In the end, I just need to be able to click that remove link to remove the row it's on. How can I accomplish that? I even tried inline JS, but that caused even stranger behavior.
P.S. I also learned that my dynamically added data-index attribute is not stored in the DOM; jQuery stores it separately, so its containing element has to be accessed by using .find()...but I can't figure out how to use .find() to access the specific individual elements I need.
Use element event(e) parameter instead this:
let i = 0;
$('#add').on('click', () => {
$('#container').append(`<div>row #${(i+1)} <a "href="#" data-index="${i}" class="remove_link">remove</a></div>`);
i++;
})
$(document).on('click', '.remove_link', (e) => {
//alert(JSON.stringify(e));
alert($(e.target).data('index'));
})
.remove_link {
color: red;
font-size: 0.8em;
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<button id="add">Add row</button>
<div id="container"></div>
For more detail read difference b/w $(this) ans event.target.
In your event handler, this represent window. You have access to e.target to get the clicked element.
This should works:
$('#container').on('click', '.remove_link', (e) => {
alert($(e.target).data('index'));
})
In my html code i change the background color of all elements with the id '#mutable', with a click on button 'blue' or 'red'.
With the third button 'load' i .append() a new HTML with the same id.
But the background color does not change for the new elements?
Whats going wrong?
fiddle
html
<div id="mutable" style="width:50px;height:50px;" class="blue">sjdfhksfh</div>
<div id="newHTML">newHTML</div>
js
$(document).ready(function() {
$('#blue').on('click', function() {
$('#mutable').trigger('blue');
});
$('#red').on('click', function() {
$('#mutable').trigger('red');
});
$('#load').live('click', function(event) {
event.preventDefault();
$('#newHTML').after('<div id=\"mutable\" style=\"width:50px;height:50px;\">...</div>');
event.stopPropagation();
});
$('#mutable').bind('red', function(e) {
$('#mutable').addClass('red').removeClass('blue');
});
$('#mutable').bind('blue', function(e) {
$('#mutable').addClass('blue').removeClass('red');
});
});
You are always creating a new div with the id #mutable. Now jQuery in terms of an ID just runs down the DOM and when it finds the first occurance of your ID, it changes it, but nothing else.
For some solutions, you could use a class .mutable instead of an id - but then every created div would be changed on click.
Or you could enumerate your IDs with a number like #mutable1, #mutable2 and so on and change your menu to select the specific div.
Or, to change just the last occurance of your dynamically created divs, use the :last - CSS- Pseudoclass.
1) id should be unique at page. And selectors like #mutable match only first element with such id -- So after pressing 'load' you create new element with the same id, but $ still match old one
2) try next ( I just change your id-selector into attr-selector which allow find all elements with id ):
$('#mutable').bind('blue', function(e) {
//alert('blue');
$('[id=mutable]:last').addClass('blue').removeClass('red');
});
http://jsfiddle.net/q3wzwr6z/
I'm working on something that creates a form based on choices you give it (text/textfield/dropdown). When I create 2 dropdowns at once I am not sure how to write in a way that the computer can tell the 2 elements aren't the same, specifically when adding new inputs in my dropdown creator it can't tell multiple dropdowns apart.
Here's the Javascript function I'm using for it
$('#submit').on('click', function() {
$("select").each(function() {
switch($(this).val()) {
case "text":
$("#form").append('<p><input type="text"/> <button class="remInput">Remove</button></p>');
break;
case "note":
$("#form").append('<p><textarea></textarea> <button class="remInput">Remove</button></p> ');
break;
case "dd":
$("#form").append('<p><select id="ddFinal"><option></option></select> <button class="remInput">Remove</button></p>');
$("#ddCreator").append('<button id="ddAddInput">Add Another Field</button> <p class="ddP"><input type="text" name="ddText"/></p> <button id="ddSubmit">Submit</button>');
break;
}
});
$('#inputDiv').empty();
$('#ddAddInput').on('click', function() {
$("p[class*='ddP']").append('<input type="text" name="ddText"/>');
});
$('#ddSubmit').on('click', function() {
$("input[name*='ddText']").each(function() {
$('#ddFinal').append('<option>'+$(this).val()+'</option>');
});
$('#ddCreator').empty();
$('#ddFinal').removeAttr('id');
});
});
The problem is with the ddAddInput function because it doesn't specify between multiple p class="ddp" elements but I'm not really sure what I should be putting instead. Hope this question makes sense.
Here's the fiddle I'm currently working on http://jsfiddle.net/me74Z/15/
There are several flaws in approach that need to be remedied.
First: - element Id's are unique in a page by definition. Use classes instead.
Next, your event handlers are being added from within another event handler. If you were to change all the ID's you have to classes and the corresponding selectors in the event handlers, you would end up compounding events.
What compounding events means is if you add the same handler twice to an element, it will fire twice.
Using event delegation would be easier. Set up your event delegation outside of the current click handler for #submit
$(document).on('click', '.ddSubmit', function() {....});
Now, as for how to handle instances, within every event handler this is the element that is the target of the event.
Using $(this) and jQuery traverse methods you can work with the target's parent and siblings or whatever you need to do.
I would suggest you wrap each level in a container to simplify the traversals. I'll call container class row so that a generic traverse from an event target can look something like:
var someField= $(this).closest('.row').find('.someField');
WHen remove button is clicked, simply remove the parent row
I am not very sure with the use of "this" [current context] in jquery.What I know is- it prevents the dom from searching all the elements, it just work on that current element, which improve performance[correct me if I am wrong].Also I am not sure when to use this and when not.
lets say, should I go for
$("span",this).slice(5).css("display", "none")
or
$("span").slice(5).css("display", "none")
both will work, but I am not very clear as how really it works.can somebody explain it with a diff/proper example, and when to use what?
[EDIT]
$(function() {
$("#clickme").click(function() {
$("span",this).slice(5).css('display', 'block');//doesn't work ? why?
$("span").slice(5).css('display', 'block');//works..why?
});
});
enter code here <span id="clickme">Click me</span>
<span>itam1</sapn>
<span>itam2</sapn>
<span>itam3</sapn>
<span>itam4</sapn>
<span>itam5</sapn>
...upto10
Usually you can use the this keyword on event handlers since it will be a reference to the element that triggered the event and other jQuery functions like $.each.
For example when handling a click event lets say:
$('.parentElement').click(function () {
$('.foo', this).hide();
});
The above code, will hide all the elements with class foo that are descendants of the currently parentElement that was clicked.
The use of the context argument of the jQuery function is the equivalent of making a call to the find method:
$(expr, context);
// is just equivalent to:
$(content).find(expr);
EDIT: Looking at your example:
$("#clickme").click(function() {
$("span",this);//... (1)
$("span");//.. (2)
});
The first line, will look for all the span elements that are inside of #clickme (its descendants), since that element was the one that triggered the click event.
The second line, will look for all the span elements on the whole page.
How it works
Lets use this HTML for the examples:
<div id="container">
<div class="column">Link 1</div>
<div class="column">Link 2</div>
</div>
<div id="footer">
Link 3Link 3
</div>
The scoping parameter of the jQuery function should only be used if you already have a cached reference to a DOM element or jQuery wrapped element set:
var $set = $('#container');
$('a', $set).hide(); // Hides all 'a' tag descendants of #container
Or in an event:
$("#container").click(function(e){
$('a', this).hide(); // Same as call above
}
But it makes no sense to use it like this:
$('a', '#container').hide()
When it should be written like this:
$('#container a').hide();
Having said all that, it is generally cleaner and clearer to just use .find() instead of using the second parameter in the jQuery function if you already have the jQuery or DOM element. The first example I gave would be written this way instead:
var $set = $('#container');
$set.find('a').hide(); // Hides all 'a' tag descendants of #container
If this one call was the only reason you grabbed the #container object, you could also write it this way since it will still scope the search to the #container element:
$("#container a").hide(); // This is the same as $('a', "#container");
Why would you scope your selections
When jQuery looks for an unscoped selector, it will search through the entire document. Depending on the complexity of the selector, this could require a lot of searching. If you know that the element you are looking for only occurs within a specific parent, it will really speed up your code to scope the selection to that parent.
Regardless of what method of scoping you choose, you should always scope your selectors whenever possible.