Destroying cloned element (can't select first instance) - javascript

I am testing some generic function building and came across this problem - cannot select first cloned element using jQuery syntax. Is it due to eventListeners not being present in the first please? If so, how to add an eventListener to something that is not present in DOM until cloned?
var cloneAndAppendCounter = 0;
function cloneAndAppend (what, target, maxClones) {
var id = what.attr('id');
var clone = what.clone(true);
var target = target;
if ( cloneAndAppendCounter < maxClones ) {
clone.attr('id', id + cloneAndAppendCounter);
clone.appendTo(target);
cloneAndAppendCounter++;
}
};
function destroyClonedElement (originalElement, when) {
var originalElementId = originalElement.attr('id');
var clonedElementId = originalElementId + cloneAndAppendCounter;
var cloned = $('#' + clonedElementId);
console.log('clonedElementId:', clonedElementId);
console.log(cloned);
if ( (cloned) && cloneAndAppendCounter > 0 ) {
cloned.remove();
cloneAndAppendCounter--;
console.log('counter: ', cloneAndAppendCounter);
};
};
$('.clone-button').click(function () {
cloneAndAppend($('#app'), $('.container'), 4);
});
$('.destroy-button').click(function () {
destroyClonedElement($('#app'));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="clone-button">clone button</button>
<button class="destroy-button">destroy button</button>
<div class="container">
<div id="app">test</div>
</div>

No, the problem is not in elements being bound or not bound to listeners.
The problem is in this line
cloneAndAppendCounter++;
or more precisely in the place you had put it, because it plays dramatic role as turned out.
So the thing is tha you append an element with a specific id to target then you increase the counter. So by the time you are pushing remove button the counter is more than the number of appended children by one and so that is why the first click has not effect - because it goes in vain.
Here is a working script(I rearranged problematic line to the place it best fits in and besides that changed initial counters):
var cloneAndAppendCounter = -1;
function cloneAndAppend (what, target, maxClones) {
var id = what.attr('id');
var clone = what.clone(true);
var target = target;
if ( cloneAndAppendCounter < maxClones ) {
cloneAndAppendCounter++;
clone.attr('id', id + cloneAndAppendCounter);
clone.appendTo(target);
// console.log('counter: ', cloneAndAppendCounter);
}
};
function destroyClonedElement (originalElement, when) {
var originalElementId = originalElement.attr('id');
var clonedElementId = originalElementId + cloneAndAppendCounter;
var cloned = $('#' + clonedElementId);
// console.log('clonedElementId:', clonedElementId);
// console.log(cloned);
if ( cloned && cloneAndAppendCounter > -1 ) {
cloned.remove();
cloneAndAppendCounter--;
// console.log('counter: ', cloneAndAppendCounter);
};
};
$('.clone-button').click(function () {
cloneAndAppend($('#app'), $('.container'), 4);
});
$(document).on('click','.destroy-button', function (e) {
e.preventDefault();
destroyClonedElement($('#app'))
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="clone-button">clone button</button>
<button class="destroy-button">destroy button</button>
<div class="container">
<div id="app">test</div>
</div>

Related

How to get the string passed to jquery? [duplicate]

$("*").click(function(){
$(this); // how can I get selector from $(this) ?
});
Is there an easy way to get selector from $(this)? There is a way to select an element by its selector, but what about getting the selector from element?
Ok, so in a comment above the question asker Fidilip said that what he/she's really after is to get the path to the current element.
Here's a script that will "climb" the DOM ancestor tree and then build fairly specific selector including any id or class attributes on the item clicked.
See it working on jsFiddle: http://jsfiddle.net/Jkj2n/209/
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script>
$(function() {
$("*").on("click", function(e) {
e.preventDefault();
var selector = $(this)
.parents()
.map(function() { return this.tagName; })
.get()
.reverse()
.concat([this.nodeName])
.join(">");
var id = $(this).attr("id");
if (id) {
selector += "#"+ id;
}
var classNames = $(this).attr("class");
if (classNames) {
selector += "." + $.trim(classNames).replace(/\s/gi, ".");
}
alert(selector);
});
});
</script>
</head>
<body>
<h1><span>I love</span> jQuery</h1>
<div>
<p>It's the <strong>BEST THING</strong> ever</p>
<button id="myButton">Button test</button>
</div>
<ul>
<li>Item one
<ul>
<li id="sub2" >Sub one</li>
<li id="sub2" class="subitem otherclass">Sub two</li>
</ul>
</li>
</ul>
</body>
</html>
For example, if you were to click the 2nd list nested list item in the HTML below, you would get the following result:
HTML>BODY>UL>LI>UL>LI#sub2.subitem.otherclass
::WARNING:: .selector has been deprecated as of version 1.7, removed as of 1.9
The jQuery object has a selector property I saw when digging in its code yesterday. Don't know if it's defined in the docs are how reliable it is (for future proofing). But it works!
$('*').selector // returns *
Edit: If you were to find the selector inside the event, that information should ideally be part of the event itself and not the element because an element could have multiple click events assigned through various selectors. A solution would be to use a wrapper to around bind(), click() etc. to add events instead of adding it directly.
jQuery.fn.addEvent = function(type, handler) {
this.bind(type, {'selector': this.selector}, handler);
};
The selector is being passed as an object's property named selector. Access it as event.data.selector.
Let's try it on some markup (http://jsfiddle.net/DFh7z/):
<p class='info'>some text and <a>a link</a></p>​
$('p a').addEvent('click', function(event) {
alert(event.data.selector); // p a
});
Disclaimer: Remember that just as with live() events, the selector property may be invalid if DOM traversal methods are used.
<div><a>a link</a></div>
The code below will NOT work, as live relies on the selector property
which in this case is a.parent() - an invalid selector.
$('a').parent().live(function() { alert('something'); });
Our addEvent method will fire, but you too will see the wrong selector - a.parent().
In collaboration with #drzaus we've come up with the following jQuery plugin.
jQuery.getSelector
!(function ($, undefined) {
/// adapted http://jsfiddle.net/drzaus/Hgjfh/5/
var get_selector = function (element) {
var pieces = [];
for (; element && element.tagName !== undefined; element = element.parentNode) {
if (element.className) {
var classes = element.className.split(' ');
for (var i in classes) {
if (classes.hasOwnProperty(i) && classes[i]) {
pieces.unshift(classes[i]);
pieces.unshift('.');
}
}
}
if (element.id && !/\s/.test(element.id)) {
pieces.unshift(element.id);
pieces.unshift('#');
}
pieces.unshift(element.tagName);
pieces.unshift(' > ');
}
return pieces.slice(1).join('');
};
$.fn.getSelector = function (only_one) {
if (true === only_one) {
return get_selector(this[0]);
} else {
return $.map(this, function (el) {
return get_selector(el);
});
}
};
})(window.jQuery);
Minified Javascript
// http://stackoverflow.com/questions/2420970/how-can-i-get-selector-from-jquery-object/15623322#15623322
!function(e,t){var n=function(e){var n=[];for(;e&&e.tagName!==t;e=e.parentNode){if(e.className){var r=e.className.split(" ");for(var i in r){if(r.hasOwnProperty(i)&&r[i]){n.unshift(r[i]);n.unshift(".")}}}if(e.id&&!/\s/.test(e.id)){n.unshift(e.id);n.unshift("#")}n.unshift(e.tagName);n.unshift(" > ")}return n.slice(1).join("")};e.fn.getSelector=function(t){if(true===t){return n(this[0])}else{return e.map(this,function(e){return n(e)})}}}(window.jQuery)
Usage and Gotchas
<html>
<head>...</head>
<body>
<div id="sidebar">
<ul>
<li>
Home
</li>
</ul>
</div>
<div id="main">
<h1 id="title">Welcome</h1>
</div>
<script type="text/javascript">
// Simple use case
$('#main').getSelector(); // => 'HTML > BODY > DIV#main'
// If there are multiple matches then an array will be returned
$('body > div').getSelector(); // => ['HTML > BODY > DIV#main', 'HTML > BODY > DIV#sidebar']
// Passing true to the method will cause it to return the selector for the first match
$('body > div').getSelector(true); // => 'HTML > BODY > DIV#main'
</script>
</body>
</html>
Fiddle w/ QUnit tests
http://jsfiddle.net/CALY5/5/
Did you try this ?
$("*").click(function(){
$(this).attr("id");
});
Try this:
$("*").click(function(event){
console.log($(event.handleObj.selector));
});
Well, I wrote this simple jQuery plugin.
This checkes id or class name, and try to give as much exact selector as possible.
jQuery.fn.getSelector = function() {
if ($(this).attr('id')) {
return '#' + $(this).attr('id');
}
if ($(this).prop("tagName").toLowerCase() == 'body') return 'body';
var myOwn = $(this).attr('class');
if (!myOwn) {
myOwn = '>' + $(this).prop("tagName");
} else {
myOwn = '.' + myOwn.split(' ').join('.');
}
return $(this).parent().getSelector() + ' ' + myOwn;
}
Just add a layer over the $ function this way:
$ = (function(jQ) {
return (function() {
var fnc = jQ.apply(this,arguments);
fnc.selector = (arguments.length>0)?arguments[0]:null;
return fnc;
});
})($);
Now you can do things like $("a").selector and will return "a" even on newer jQuery versions.
http://www.selectorgadget.com/ is a bookmarklet designed explicitly for this use case.
That said, I agree with most other people in that you should just learn CSS selectors yourself, trying to generate them with code is not sustainable. :)
I added some fixes to #jessegavin's fix.
This will return right away if there is an ID on the element. I also added a name attribute check and a nth-child selector in case a element has no id, class, or name.
The name might need scoping in case there a multiple forms on the page and have similar inputs, but I didn't handle that yet.
function getSelector(el){
var $el = $(el);
var id = $el.attr("id");
if (id) { //"should" only be one of these if theres an ID
return "#"+ id;
}
var selector = $el.parents()
.map(function() { return this.tagName; })
.get().reverse().join(" ");
if (selector) {
selector += " "+ $el[0].nodeName;
}
var classNames = $el.attr("class");
if (classNames) {
selector += "." + $.trim(classNames).replace(/\s/gi, ".");
}
var name = $el.attr('name');
if (name) {
selector += "[name='" + name + "']";
}
if (!name){
var index = $el.index();
if (index) {
index = index + 1;
selector += ":nth-child(" + index + ")";
}
}
return selector;
}
I've released a jQuery plugin: jQuery Selectorator, you can get selector like this.
$("*").on("click", function(){
alert($(this).getSelector().join("\n"));
return false;
});
I was getting multiple elements even after above solutions, so i extended dds1024 work, for even more pin-pointing dom element.
e.g. DIV:nth-child(1) DIV:nth-child(3) DIV:nth-child(1) ARTICLE:nth-child(1) DIV:nth-child(1) DIV:nth-child(8) DIV:nth-child(2) DIV:nth-child(1) DIV:nth-child(2) DIV:nth-child(1) H4:nth-child(2)
Code:
function getSelector(el)
{
var $el = jQuery(el);
var selector = $el.parents(":not(html,body)")
.map(function() {
var i = jQuery(this).index();
i_str = '';
if (typeof i != 'undefined')
{
i = i + 1;
i_str += ":nth-child(" + i + ")";
}
return this.tagName + i_str;
})
.get().reverse().join(" ");
if (selector) {
selector += " "+ $el[0].nodeName;
}
var index = $el.index();
if (typeof index != 'undefined') {
index = index + 1;
selector += ":nth-child(" + index + ")";
}
return selector;
}
Taking in account some answers read here I'd like to propose this:
function getSelectorFromElement($el) {
if (!$el || !$el.length) {
return ;
}
function _getChildSelector(index) {
if (typeof index === 'undefined') {
return '';
}
index = index + 1;
return ':nth-child(' + index + ')';
}
function _getIdAndClassNames($el) {
var selector = '';
// attach id if exists
var elId = $el.attr('id');
if(elId){
selector += '#' + elId;
}
// attach class names if exists
var classNames = $el.attr('class');
if(classNames){
selector += '.' + classNames.replace(/^\s+|\s+$/g, '').replace(/\s/gi, '.');
}
return selector;
}
// get all parents siblings index and element's tag name,
// except html and body elements
var selector = $el.parents(':not(html,body)')
.map(function() {
var parentIndex = $(this).index();
return this.tagName + _getChildSelector(parentIndex);
})
.get()
.reverse()
.join(' ');
if (selector) {
// get node name from the element itself
selector += ' ' + $el[0].nodeName +
// get child selector from element ifself
_getChildSelector($el.index());
}
selector += _getIdAndClassNames($el);
return selector;
}
Maybe useful to create a jQuery plugin?
This can get you selector path of clicked HTML element-
$("*").on("click", function() {
let selectorPath = $(this).parents().map(function () {return this.tagName;}).get().reverse().join("->");
alert(selectorPath);
return false;
});
Are you trying to get the name of the current tag that was clicked?
If so, do this..
$("*").click(function(){
alert($(this)[0].nodeName);
});
You can't really get the "selector", the "selector" in your case is *.
Javascript code for the same, in case any one needs, as i needed it. This just the translation only of the above selected answer.
<script type="text/javascript">
function getAllParents(element){
var a = element;
var els = [];
while (a && a.nodeName != "#document") {
els.unshift(a.nodeName);
a = a.parentNode;
}
return els.join(" ");
}
function getJquerySelector(element){
var selector = getAllParents(element);
/* if(selector){
selector += " " + element.nodeName;
} */
var id = element.getAttribute("id");
if(id){
selector += "#" + id;
}
var classNames = element.getAttribute("class");
if(classNames){
selector += "." + classNames.replace(/^\s+|\s+$/g, '').replace(/\s/gi, ".");
}
console.log(selector);
alert(selector);
return selector;
}
</script>
Thank you p1nox!
My problem was to put focus back on an ajax call that was modifying part of the form.
$.ajax({ url : "ajax_invite_load.php",
async : true,
type : 'POST',
data : ...
dataType : 'html',
success : function(html, statut) {
var focus = $(document.activeElement).getSelector();
$td_left.html(html);
$(focus).focus();
}
});
I just needed to encapsulate your function in a jQuery plugin:
!(function ($, undefined) {
$.fn.getSelector = function () {
if (!this || !this.length) {
return ;
}
function _getChildSelector(index) {
if (typeof index === 'undefined') {
return '';
}
index = index + 1;
return ':nth-child(' + index + ')';
}
function _getIdAndClassNames($el) {
var selector = '';
// attach id if exists
var elId = $el.attr('id');
if(elId){
selector += '#' + elId;
}
// attach class names if exists
var classNames = $el.attr('class');
if(classNames){
selector += '.' + classNames.replace(/^\s+|\s+$/g, '').replace(/\s/gi, '.');
}
return selector;
}
// get all parents siblings index and element's tag name,
// except html and body elements
var selector = this.parents(':not(html,body)')
.map(function() {
var parentIndex = $(this).index();
return this.tagName + _getChildSelector(parentIndex);
})
.get()
.reverse()
.join(' ');
if (selector) {
// get node name from the element itself
selector += ' ' + this[0].nodeName +
// get child selector from element ifself
_getChildSelector(this.index());
}
selector += _getIdAndClassNames(this);
return selector;
}
})(window.jQuery);
This won't show you the DOM path, but it will output a string representation of what you see in eg chrome debugger, when viewing an object.
$('.mybtn').click( function(event){
console.log("%s", this); // output: "button.mybtn"
});
https://developer.chrome.com/devtools/docs/console-api#consolelogobject-object
How about:
var selector = "*"
$(selector).click(function() {
alert(selector);
});
I don't believe jQuery store the selector text that was used. After all, how would that work if you did something like this:
$("div").find("a").click(function() {
// what would expect the 'selector' to be here?
});
The best answer would be
var selector = '#something';
$(selector).anything(function(){
console.log(selector);
});

Jquery Remove Attributes from element (in loop) only work the first time

I have a function that create and removes all data-attributes from 18 images elements (#scelta1, #scelta2, etc) it will run every time the user click one of these images:
$(document).ready(function () {
$('#scelta6']").on("click", function() {
// For simplicity #scelta6,
// the real function gets the id based on the element you click
$('#scelta6'+).attr({'data-a': foo,
'data-b': bar,
[a lot of data-] });
// All the variables is taken from the dropdowns choices of the user in a
// separate function
[do something]
var conta = 1;
var fiamma;
while (conta < 19) {
fiamma = $('#scelta'+conta);
$.each($(fiamma).data(), function(i){ $(fiamma).removeAttr("data-" + i); });
console.log(conta); // Just to know if the function is running
conta++;
}
}
Now the problem: on a fresh new loaded page if I click on one these elements the function above will run without any problem and removes all the data-attributes from all elements. When I will click again on one of these elements all the all the data-attributes remain in their place!
Why this function works only for the first time?
I missed something?
EDIT
The data will be removed in every element if the first time the function run and have in ALL the elements the data set.
For example:
I will set data to #scelta1, #scelta2, #scelta3, the run the function, it will remove the data from all. Then I set again it in #scelta2, click and all the data will be removed. Then I set again data in #scelta1, #scelta2, #scelta3, click and all the data will be removed from them all. But if I set data to #scelta2 and #scelta4 it will remove ONLY the data in #scelta2...
EDIT: it seems that using attr to set data attributes was causing the bug (i verified and the behaviour was like you described). With setting the attributes with data() it seems to work all right.
$(document).ready(function () {
//setting data with attr seems to be the cause of the bug here, use data() instead
$('.sceltaButt').on("click", function() {
//$('#' + $(this).attr('id')).attr({'data-a': 'foo', 'data-b': 'bar'});
$('#' + $(this).attr('id')).data('a', 'foo').data('b', 'bar');
showData();
});
//then you don't need to remove them manually anymore, removeData is enough
$('#removeData').on("click", function() {
var conta = 1;
var fiamma;
while (conta < 7) {
fiamma = $('#scelta'+conta);
$(fiamma).removeData();
//console.log(conta);
conta++;
}
showData();
});
//now this version doesn't work anymore because we use data()
$('#removeWithoutData').on("click", function() {
var conta = 1;
var fiamma;
while (conta < 7) {
fiamma = $('#scelta'+conta);
$.each($(fiamma).data(), function(i){
$(fiamma).removeAttr("data-" + i);
});
//console.log(conta);
conta++;
}
showData();
});
function showData(){
var result = '<br>';
$('.sceltaButt').each(function(){
/*$.each($(this)[0].attributes, function(){
result += ' ' + this.name + ':' + this.value;
});*/
$.each($(this).data(), function(name){
result += ' ' + name + ':' + this;
});
result += '<br>';
});
$('#result').html(result);
}
showData();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="sceltaButt" id="scelta1">ADD TO 1</button>
<button class="sceltaButt" id="scelta2" data-c="test">ADD TO 2</button>
<button class="sceltaButt" id="scelta3">ADD TO 3</button>
<button class="sceltaButt" id="scelta4" data-d="test2">ADD TO 4</button>
<button class="sceltaButt" id="scelta5">ADD TO 5</button>
<button class="sceltaButt" id="scelta6">ADD TO 6</button>
<button id="removeData">CLICK ME TO REMOVE (with removeData)</button>
<button id="removeWithoutData">CLICK ME TO REMOVE (without removeData)</button>
<div>CURRENT DATA: <span id="result"></span></div>

jQuery Tree issue - add first child li

I have 2 columns, on the left side a team with users, on the right column, will be displayed the users i have selected. so everything its working but i'm trying to implement a new feature as follow:
I have 2 list level like a tree (only 2 levels). When i click on a user, i'm able to select it sending to the right column. Also, when i click (single click) on the first level (team name), the second level (users) appear as toggle jquery function. i need so, when i double click on a team (level 1) all users on that tree turns selected and go to column on the right side.
Also, when i click on the team (first level) on the right side, all the users get removed back.
My code to add the users jquery current is:
$(document).ready(function () {
var maxAllowed = 10000;
var $selectTable = $("#mytable");
var $selectList = $("#selected_users ul")
$("#max-count").html(maxAllowed);
var getActivated = function () {
var activated = new Array();
$selectTable.find('input[type="checkbox"]:checked').closest("li").each(function () {
var $obj = new Object;
var currentBox = $(this).find('input[type="checkbox"]');
$obj.id = currentBox.val();
$obj.boxid = currentBox.attr("id");
$obj.name = $(this).find("label").text();
activated.push($obj);
});
return activated;
}
var updateActiveList = function () {
// Truncate list
$selectList.html("");
$(getActivated()).each(function () {
$selectList.append("<li><a href='#' class='remove' data-id='" + this.id + "' data-box-id='" + this.boxid + "'>" + this.name + "</li></a>");
});
}
var countActivated = function () {
return getActivated().length;
}
$('#view').click(function () {
allIds = new Array();
getActivated().each(function () {
allIds.push($(this).attr("id"));
});
alert(allIds);
});
$selectList.on("click", "a.remove", function () {
$('#' + $(this).data("box-id")).prop("checked", false);
updateActiveList();
});
$selectTable.on("change", 'input[type="checkbox"]', function (event) {
if ($(this).is(":checked") && countActivated() > maxAllowed) {
event.preventDefault();
console.log("max reached!");
$(this).prop("checked", false);
}
updateActiveList();
});
});
Here's a jsFiddle with working example:
http://jsfiddle.net/muzkle/LMbV3/7/
Thanks all!
EDIT
Hi, i just added a code to separate single click from double click. So when the user single click, will open the tree. now i need when the user double click on the first level, add both (first level and they're childrens to the right side.
Follow code for single and double clicks:
alreadyclicked=false;
$(document).ready(function () {
$('#mytable').on('click', '.toggle', function (ul) {
//Gets all <tr>'s of greater depth
//below element in the table
var findChildren = function (ul) {
var depth = ul.data('depth');
return ul.nextUntil($('ul').filter(function () {
return $(this).data('depth') <= depth;
}));
};
var el = $(this);
var ul = el.closest('ul'); //Get <tr> parent of toggle button
var children = findChildren(ul);
var el=$(this);
if (alreadyclicked){
alreadyclicked=false; // reset
clearTimeout(alreadyclickedTimeout); // prevent this from happening
}else{
alreadyclicked=true;
alreadyclickedTimeout=setTimeout(function(){
alreadyclicked=false; // reset when it happens
//Remove already collapsed nodes from children so that we don't
//make them visible.
//(Confused? Remove this code and close Item 2, close Item 1
//then open Item 1 again, then you will understand)
var subnodes = children.filter('.expand');
subnodes.each(function () {
var subnode = $(this);
var subnodeChildren = findChildren(subnode);
children = children.not(subnodeChildren);
});
//Change icon and hide/show children
if (ul.hasClass('collapse')) {
ul.removeClass('collapse').addClass('expand');
children.hide();
} else {
ul.removeClass('expand').addClass('collapse');
children.show();
}
return children;
// do what needs to happen on single click.
// use el instead of $(this) because $(this) is
// no longer the element
},300); // <-- dblclick tolerance here
}
return false;
});
});
And new jsFiddle is: http://jsfiddle.net/muzkle/LMbV3/8/
To distinguish different groups I am wrapping each group/section in a wrapper div with class .wrapper
<div class="wrapper">
.
.
</div>
Also I attached a double click event to .wrapper and currently I have made it to alert its inner labels.Just write some additional code to add these labels to the right side like you are currently adding one element on click.Below is the code with jQuery .dblclick() function which attaches a double-click event to .wrapper.
$('.wrapper').dblclick(function(){
$(this).find('label').each(function(){
alert($(this).text());
});
});
Check this fiddle

How to loop through ids with same prefix

I have this section of code that saves a list item on click to localStorage based on the list item's id number. On a different page I am then able to load whatever list items were saved.
var save1075 = $('#store-1075').get(0);
$('#store-1075').on('click', function () {
var storeStorageKey1075 = $(this).attr('id');
localStorage.setItem('storeStorageKey1075', $(this).attr('id'));
localStorage.setItem('storeStorageKey1075', this.innerHTML);
});
if ( localStorage.getItem('storeStorageKey1075') ) {
save1075.innerHTML = localStorage.getItem('storeStorageKey1075');
}
var storeStorageKey1075 = $(this).attr('id');
if ( localStorage.getItem('storeStorageKey1075') ) {
storeStorageKey1075 = localStorage.getItem('storeStorageKey1075');
}
Right now I have to repeat that chunk of code for every individual id number and I'm trying to instead make the id number a variable that loops through all possible id numbers when clicked but only saves the id of that specific one. Maybe something along the lines of this:
var id = //some sort of loop or array of possible ids.
var save = $('#store-'+id).get(0);
$('#store-'+id).on('click', function () {
var storeStorageKey = $(this).attr('id');
localStorage.setItem('storeStorageKey', $(this).attr('id'));
localStorage.setItem('storeStorageKey', this.innerHTML);
});
if ( localStorage.getItem('storeStorageKey') ) {
save.innerHTML = localStorage.getItem('storeStorageKey');
}
var storeStorageKey = $(this).attr('id');
if ( localStorage.getItem('storeStorageKey') ) {
storeStorageKey = localStorage.getItem('storeStorageKey');
}
Each of the list items has the same prefix of "store-" and the numbers are in no specific order but randomly generated each time a new store location is created.
The obvious thing to do is to add a class but you could also:
var elems = $( "[id^='store']")
elems.on( "click", function() {//Save html on click
localStorage.setItem( this.id, this.innerHTML );
});
//Retrieve html
elems.each( function() {
var html = localStorage.getItem( this.id );
if( html ) {
$(this).html( html );
}
});
This loops over all keys that begin with storeStorageKey:
for (var i=0; i<localStorage.length; i++) {
var key = localStorage.key(i);
if (/^storeStorageKey/.test(key)) {
var item = localStorage.getItem(key);
// do something with item
}
}
Instead of using a prefix in the id I'd suggest adding a class to indicate that an item is storable:
<div class="storable" id="store-1234">...</div>
then you can use the class to select all the storable things in one go:
$('.storable').on('click', function () {
var storeStorageKey = $(this).attr('id');
...
});

How can I get selector from jQuery object

$("*").click(function(){
$(this); // how can I get selector from $(this) ?
});
Is there an easy way to get selector from $(this)? There is a way to select an element by its selector, but what about getting the selector from element?
Ok, so in a comment above the question asker Fidilip said that what he/she's really after is to get the path to the current element.
Here's a script that will "climb" the DOM ancestor tree and then build fairly specific selector including any id or class attributes on the item clicked.
See it working on jsFiddle: http://jsfiddle.net/Jkj2n/209/
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script>
$(function() {
$("*").on("click", function(e) {
e.preventDefault();
var selector = $(this)
.parents()
.map(function() { return this.tagName; })
.get()
.reverse()
.concat([this.nodeName])
.join(">");
var id = $(this).attr("id");
if (id) {
selector += "#"+ id;
}
var classNames = $(this).attr("class");
if (classNames) {
selector += "." + $.trim(classNames).replace(/\s/gi, ".");
}
alert(selector);
});
});
</script>
</head>
<body>
<h1><span>I love</span> jQuery</h1>
<div>
<p>It's the <strong>BEST THING</strong> ever</p>
<button id="myButton">Button test</button>
</div>
<ul>
<li>Item one
<ul>
<li id="sub2" >Sub one</li>
<li id="sub2" class="subitem otherclass">Sub two</li>
</ul>
</li>
</ul>
</body>
</html>
For example, if you were to click the 2nd list nested list item in the HTML below, you would get the following result:
HTML>BODY>UL>LI>UL>LI#sub2.subitem.otherclass
::WARNING:: .selector has been deprecated as of version 1.7, removed as of 1.9
The jQuery object has a selector property I saw when digging in its code yesterday. Don't know if it's defined in the docs are how reliable it is (for future proofing). But it works!
$('*').selector // returns *
Edit: If you were to find the selector inside the event, that information should ideally be part of the event itself and not the element because an element could have multiple click events assigned through various selectors. A solution would be to use a wrapper to around bind(), click() etc. to add events instead of adding it directly.
jQuery.fn.addEvent = function(type, handler) {
this.bind(type, {'selector': this.selector}, handler);
};
The selector is being passed as an object's property named selector. Access it as event.data.selector.
Let's try it on some markup (http://jsfiddle.net/DFh7z/):
<p class='info'>some text and <a>a link</a></p>​
$('p a').addEvent('click', function(event) {
alert(event.data.selector); // p a
});
Disclaimer: Remember that just as with live() events, the selector property may be invalid if DOM traversal methods are used.
<div><a>a link</a></div>
The code below will NOT work, as live relies on the selector property
which in this case is a.parent() - an invalid selector.
$('a').parent().live(function() { alert('something'); });
Our addEvent method will fire, but you too will see the wrong selector - a.parent().
In collaboration with #drzaus we've come up with the following jQuery plugin.
jQuery.getSelector
!(function ($, undefined) {
/// adapted http://jsfiddle.net/drzaus/Hgjfh/5/
var get_selector = function (element) {
var pieces = [];
for (; element && element.tagName !== undefined; element = element.parentNode) {
if (element.className) {
var classes = element.className.split(' ');
for (var i in classes) {
if (classes.hasOwnProperty(i) && classes[i]) {
pieces.unshift(classes[i]);
pieces.unshift('.');
}
}
}
if (element.id && !/\s/.test(element.id)) {
pieces.unshift(element.id);
pieces.unshift('#');
}
pieces.unshift(element.tagName);
pieces.unshift(' > ');
}
return pieces.slice(1).join('');
};
$.fn.getSelector = function (only_one) {
if (true === only_one) {
return get_selector(this[0]);
} else {
return $.map(this, function (el) {
return get_selector(el);
});
}
};
})(window.jQuery);
Minified Javascript
// http://stackoverflow.com/questions/2420970/how-can-i-get-selector-from-jquery-object/15623322#15623322
!function(e,t){var n=function(e){var n=[];for(;e&&e.tagName!==t;e=e.parentNode){if(e.className){var r=e.className.split(" ");for(var i in r){if(r.hasOwnProperty(i)&&r[i]){n.unshift(r[i]);n.unshift(".")}}}if(e.id&&!/\s/.test(e.id)){n.unshift(e.id);n.unshift("#")}n.unshift(e.tagName);n.unshift(" > ")}return n.slice(1).join("")};e.fn.getSelector=function(t){if(true===t){return n(this[0])}else{return e.map(this,function(e){return n(e)})}}}(window.jQuery)
Usage and Gotchas
<html>
<head>...</head>
<body>
<div id="sidebar">
<ul>
<li>
Home
</li>
</ul>
</div>
<div id="main">
<h1 id="title">Welcome</h1>
</div>
<script type="text/javascript">
// Simple use case
$('#main').getSelector(); // => 'HTML > BODY > DIV#main'
// If there are multiple matches then an array will be returned
$('body > div').getSelector(); // => ['HTML > BODY > DIV#main', 'HTML > BODY > DIV#sidebar']
// Passing true to the method will cause it to return the selector for the first match
$('body > div').getSelector(true); // => 'HTML > BODY > DIV#main'
</script>
</body>
</html>
Fiddle w/ QUnit tests
http://jsfiddle.net/CALY5/5/
Did you try this ?
$("*").click(function(){
$(this).attr("id");
});
Try this:
$("*").click(function(event){
console.log($(event.handleObj.selector));
});
Well, I wrote this simple jQuery plugin.
This checkes id or class name, and try to give as much exact selector as possible.
jQuery.fn.getSelector = function() {
if ($(this).attr('id')) {
return '#' + $(this).attr('id');
}
if ($(this).prop("tagName").toLowerCase() == 'body') return 'body';
var myOwn = $(this).attr('class');
if (!myOwn) {
myOwn = '>' + $(this).prop("tagName");
} else {
myOwn = '.' + myOwn.split(' ').join('.');
}
return $(this).parent().getSelector() + ' ' + myOwn;
}
Just add a layer over the $ function this way:
$ = (function(jQ) {
return (function() {
var fnc = jQ.apply(this,arguments);
fnc.selector = (arguments.length>0)?arguments[0]:null;
return fnc;
});
})($);
Now you can do things like $("a").selector and will return "a" even on newer jQuery versions.
http://www.selectorgadget.com/ is a bookmarklet designed explicitly for this use case.
That said, I agree with most other people in that you should just learn CSS selectors yourself, trying to generate them with code is not sustainable. :)
I added some fixes to #jessegavin's fix.
This will return right away if there is an ID on the element. I also added a name attribute check and a nth-child selector in case a element has no id, class, or name.
The name might need scoping in case there a multiple forms on the page and have similar inputs, but I didn't handle that yet.
function getSelector(el){
var $el = $(el);
var id = $el.attr("id");
if (id) { //"should" only be one of these if theres an ID
return "#"+ id;
}
var selector = $el.parents()
.map(function() { return this.tagName; })
.get().reverse().join(" ");
if (selector) {
selector += " "+ $el[0].nodeName;
}
var classNames = $el.attr("class");
if (classNames) {
selector += "." + $.trim(classNames).replace(/\s/gi, ".");
}
var name = $el.attr('name');
if (name) {
selector += "[name='" + name + "']";
}
if (!name){
var index = $el.index();
if (index) {
index = index + 1;
selector += ":nth-child(" + index + ")";
}
}
return selector;
}
I've released a jQuery plugin: jQuery Selectorator, you can get selector like this.
$("*").on("click", function(){
alert($(this).getSelector().join("\n"));
return false;
});
I was getting multiple elements even after above solutions, so i extended dds1024 work, for even more pin-pointing dom element.
e.g. DIV:nth-child(1) DIV:nth-child(3) DIV:nth-child(1) ARTICLE:nth-child(1) DIV:nth-child(1) DIV:nth-child(8) DIV:nth-child(2) DIV:nth-child(1) DIV:nth-child(2) DIV:nth-child(1) H4:nth-child(2)
Code:
function getSelector(el)
{
var $el = jQuery(el);
var selector = $el.parents(":not(html,body)")
.map(function() {
var i = jQuery(this).index();
i_str = '';
if (typeof i != 'undefined')
{
i = i + 1;
i_str += ":nth-child(" + i + ")";
}
return this.tagName + i_str;
})
.get().reverse().join(" ");
if (selector) {
selector += " "+ $el[0].nodeName;
}
var index = $el.index();
if (typeof index != 'undefined') {
index = index + 1;
selector += ":nth-child(" + index + ")";
}
return selector;
}
Taking in account some answers read here I'd like to propose this:
function getSelectorFromElement($el) {
if (!$el || !$el.length) {
return ;
}
function _getChildSelector(index) {
if (typeof index === 'undefined') {
return '';
}
index = index + 1;
return ':nth-child(' + index + ')';
}
function _getIdAndClassNames($el) {
var selector = '';
// attach id if exists
var elId = $el.attr('id');
if(elId){
selector += '#' + elId;
}
// attach class names if exists
var classNames = $el.attr('class');
if(classNames){
selector += '.' + classNames.replace(/^\s+|\s+$/g, '').replace(/\s/gi, '.');
}
return selector;
}
// get all parents siblings index and element's tag name,
// except html and body elements
var selector = $el.parents(':not(html,body)')
.map(function() {
var parentIndex = $(this).index();
return this.tagName + _getChildSelector(parentIndex);
})
.get()
.reverse()
.join(' ');
if (selector) {
// get node name from the element itself
selector += ' ' + $el[0].nodeName +
// get child selector from element ifself
_getChildSelector($el.index());
}
selector += _getIdAndClassNames($el);
return selector;
}
Maybe useful to create a jQuery plugin?
This can get you selector path of clicked HTML element-
$("*").on("click", function() {
let selectorPath = $(this).parents().map(function () {return this.tagName;}).get().reverse().join("->");
alert(selectorPath);
return false;
});
Are you trying to get the name of the current tag that was clicked?
If so, do this..
$("*").click(function(){
alert($(this)[0].nodeName);
});
You can't really get the "selector", the "selector" in your case is *.
Javascript code for the same, in case any one needs, as i needed it. This just the translation only of the above selected answer.
<script type="text/javascript">
function getAllParents(element){
var a = element;
var els = [];
while (a && a.nodeName != "#document") {
els.unshift(a.nodeName);
a = a.parentNode;
}
return els.join(" ");
}
function getJquerySelector(element){
var selector = getAllParents(element);
/* if(selector){
selector += " " + element.nodeName;
} */
var id = element.getAttribute("id");
if(id){
selector += "#" + id;
}
var classNames = element.getAttribute("class");
if(classNames){
selector += "." + classNames.replace(/^\s+|\s+$/g, '').replace(/\s/gi, ".");
}
console.log(selector);
alert(selector);
return selector;
}
</script>
Thank you p1nox!
My problem was to put focus back on an ajax call that was modifying part of the form.
$.ajax({ url : "ajax_invite_load.php",
async : true,
type : 'POST',
data : ...
dataType : 'html',
success : function(html, statut) {
var focus = $(document.activeElement).getSelector();
$td_left.html(html);
$(focus).focus();
}
});
I just needed to encapsulate your function in a jQuery plugin:
!(function ($, undefined) {
$.fn.getSelector = function () {
if (!this || !this.length) {
return ;
}
function _getChildSelector(index) {
if (typeof index === 'undefined') {
return '';
}
index = index + 1;
return ':nth-child(' + index + ')';
}
function _getIdAndClassNames($el) {
var selector = '';
// attach id if exists
var elId = $el.attr('id');
if(elId){
selector += '#' + elId;
}
// attach class names if exists
var classNames = $el.attr('class');
if(classNames){
selector += '.' + classNames.replace(/^\s+|\s+$/g, '').replace(/\s/gi, '.');
}
return selector;
}
// get all parents siblings index and element's tag name,
// except html and body elements
var selector = this.parents(':not(html,body)')
.map(function() {
var parentIndex = $(this).index();
return this.tagName + _getChildSelector(parentIndex);
})
.get()
.reverse()
.join(' ');
if (selector) {
// get node name from the element itself
selector += ' ' + this[0].nodeName +
// get child selector from element ifself
_getChildSelector(this.index());
}
selector += _getIdAndClassNames(this);
return selector;
}
})(window.jQuery);
This won't show you the DOM path, but it will output a string representation of what you see in eg chrome debugger, when viewing an object.
$('.mybtn').click( function(event){
console.log("%s", this); // output: "button.mybtn"
});
https://developer.chrome.com/devtools/docs/console-api#consolelogobject-object
How about:
var selector = "*"
$(selector).click(function() {
alert(selector);
});
I don't believe jQuery store the selector text that was used. After all, how would that work if you did something like this:
$("div").find("a").click(function() {
// what would expect the 'selector' to be here?
});
The best answer would be
var selector = '#something';
$(selector).anything(function(){
console.log(selector);
});

Categories

Resources