toggle class with pure javascript [duplicate] - javascript

I'm looking for a way to convert this jQuery code (which is used in responsive menu section) to pure JavaScript.
If it's hard to implement it's OK to use other JavaScript frameworks.
$('.btn-navbar').click(function()
{
$('.container-fluid:first').toggleClass('menu-hidden');
$('#menu').toggleClass('hidden-phone');
if (typeof masonryGallery != 'undefined')
masonryGallery();
});

2014 answer: classList.toggle() is the standard and supported by most browsers.
Older browsers can use use classlist.js for classList.toggle():
var menu = document.querySelector('.menu') // Using a class instead, see note below.
menu.classList.toggle('hidden-phone');
As an aside, you shouldn't be using IDs (they leak globals into the JS window object).

Here is solution implemented with ES6
const toggleClass = (el, className) => el.classList.toggle(className);
usage example
toggleClass(document.querySelector('div.active'), 'active'); // The div container will not have the 'active' class anymore

Take a look at this example: JS Fiddle
function toggleClass(element, className){
if (!element || !className){
return;
}
var classString = element.className, nameIndex = classString.indexOf(className);
if (nameIndex == -1) {
classString += ' ' + className;
}
else {
classString = classString.substr(0, nameIndex) + classString.substr(nameIndex+className.length);
}
element.className = classString;
}

don't need regex just use classlist
var id=document.getElementById('myButton');
function toggle(el,classname){
if(el.classList.contains(classname)){
el.classList.remove(classname)
}
else{
el.classList.add(classname)
}
}
id.addEventListener('click',(e)=>{
toggle(e.target,'red')
})
.red{
background:red
}
<button id="myButton">Switch</button>
Simple Usage above Example
var id=document.getElementById('myButton');
function toggle(el,classname){
el.classList.toggle(classname)
}
id.addEventListener('click',(e)=>{
toggle(e.target,'red')
})
.red{
background:red
}
<button id="myButton">Switch</button>

This one works in earlier versions of IE also.
function toogleClass(ele, class1) {
var classes = ele.className;
var regex = new RegExp('\\b' + class1 + '\\b');
var hasOne = classes.match(regex);
class1 = class1.replace(/\s+/g, '');
if (hasOne)
ele.className = classes.replace(regex, '');
else
ele.className = classes + class1;
}
.red {
background-color: red
}
div {
width: 100px;
height: 100px;
margin-bottom: 10px;
border: 1px solid black;
}
<div class="does red redAnother " onclick="toogleClass(this, 'red')"></div>
<div class="does collapse navbar-collapse " onclick="toogleClass(this, 'red')"></div>

This is perhaps more succinct:
function toggle(element, klass) {
var classes = element.className.match(/\S+/g) || [],
index = classes.indexOf(klass);
index >= 0 ? classes.splice(index, 1) : classes.push(klass);
element.className = classes.join(' ');
}

If anyone looking to toggle class on mousehover/mousleave using Javascript here is the code for it
function changeColor() {
this.classList.toggle('red');
this.classList.toggle('green');
}
document.querySelector('#btn').addEventListener('mouseenter', changeColor);
document.querySelector('#btn').addEventListener('mouseleave', changeColor );
Demo Fiddle link: https://jsfiddle.net/eg2k7mLj/1/
Source: Toggle Class (Javascript based, without jQuery)

Just for legacy reasons:
function toggleClassName(elementClassName, className) {
const originalClassNames = elementClassName.split(/\s+/);
const newClassNames = [];
let found = false;
for (let index = 0; index < originalClassNames.length; index++) {
if (originalClassNames[index] === '') {
continue;
}
if (originalClassNames[index] === className) {
found = true;
continue;
}
newClassNames.push(originalClassNames[index]);
}
if (!found) {
newClassNames.push(className);
}
return newClassNames.join(' ');
}
console.assert(toggleClassName('', 'foo') === 'foo');
console.assert(toggleClassName('foo', 'bar') === 'foo bar');
console.assert(toggleClassName('foo bar', 'bar') === 'foo');
console.assert(toggleClassName('bar foo', 'bar') === 'foo');
console.assert(toggleClassName('foo bar baz', 'bar') === 'foo baz');
console.assert(toggleClassName('foo-bar', 'foo') === 'foo-bar foo');
console.assert(toggleClassName('bar foo-bar', 'bar') === 'foo-bar');
console.assert(toggleClassName('bar bar bar foo-bar bar', 'bar') === 'foo-bar');
console.assert(toggleClassName(" \n\nbar-bar \nbar-baz foo", 'bar-baz') === 'bar-bar foo');
element.className = toggleClassName(element.className, 'foo');

Try this (hopefully it will work):
// mixin (functionality) for toggle class
function hasClass(ele, clsName) {
var el = ele.className;
el = el.split(' ');
if(el.indexOf(clsName) > -1){
var cIndex = el.indexOf(clsName);
el.splice(cIndex, 1);
ele.className = " ";
el.forEach(function(item, index){
ele.className += " " + item;
})
}
else {
el.push(clsName);
ele.className = " ";
el.forEach(function(item, index){
ele.className += " " + item;
})
}
}
// get all DOM element that we need for interactivity.
var btnNavbar = document.getElementsByClassName('btn-navbar')[0];
var containerFluid = document.querySelector('.container-fluid:first');
var menu = document.getElementById('menu');
// on button click job
btnNavbar.addEventListener('click', function(){
hasClass(containerFluid, 'menu-hidden');
hasClass(menu, 'hidden-phone');
})`enter code here`

Here is a code for IE >= 9 by using split(" ") on the className :
function toggleClass(element, className) {
var arrayClass = element.className.split(" ");
var index = arrayClass.indexOf(className);
if (index === -1) {
if (element.className !== "") {
element.className += ' '
}
element.className += className;
} else {
arrayClass.splice(index, 1);
element.className = "";
for (var i = 0; i < arrayClass.length; i++) {
element.className += arrayClass[i];
if (i < arrayClass.length - 1) {
element.className += " ";
}
}
}
}

If you want to toggle a class to an element using native solution, you could try this suggestion. I have tasted it in different cases, with or without other classes onto the element, and I think it works pretty much:
(function(objSelector, objClass){
document.querySelectorAll(objSelector).forEach(function(o){
o.addEventListener('click', function(e){
var $this = e.target,
klass = $this.className,
findClass = new RegExp('\\b\\s*' + objClass + '\\S*\\s?', 'g');
if( !findClass.test( $this.className ) )
if( klass )
$this.className = klass + ' ' + objClass;
else
$this.setAttribute('class', objClass);
else
{
klass = klass.replace( findClass, '' );
if(klass) $this.className = klass;
else $this.removeAttribute('class');
}
});
});
})('.yourElemetnSelector', 'yourClass');

I know that I am late but, I happen to see this and I have a suggestion..
For those looking for cross-browser support, I wouldn't recommend class toggling via JS.
It may be a little more work but it is more supported through all browsers.
document.getElementById("myButton").addEventListener('click', themeswitch);
function themeswitch() {
const Body = document.body
if (Body.style.backgroundColor === 'white') {
Body.style.backgroundColor = 'black';
} else {
Body.style.backgroundColor = 'white';
}
}
body {
background: white;
}
<button id="myButton">Switch</button>

function navbarToggler() {
const collapseBtn = document.querySelector('.collapseBtn').classList
collapseBtn.toggle('collapse')
}

Related

How to get the highest level parent of clicked element [duplicate]

HTML
<body>
<div class="lol">
<a class="rightArrow" href="javascriptVoid:(0);" title"Next image">
</div>
</body>
Pseudo Code
$(".rightArrow").click(function() {
rightArrowParents = this.dom(); //.dom(); is the pseudo function ... it should show the whole
alert(rightArrowParents);
});
Alert message would be:
body div.lol a.rightArrow
How can I get this with javascript/jquery?
Here is a native JS version that returns a jQuery path. I'm also adding IDs for elements if they have them. This would give you the opportunity to do the shortest path if you see an id in the array.
var path = getDomPath(element);
console.log(path.join(' > '));
Outputs
body > section:eq(0) > div:eq(3) > section#content > section#firehose > div#firehoselist > article#firehose-46813651 > header > h2 > span#title-46813651
Here is the function.
function getDomPath(el) {
var stack = [];
while ( el.parentNode != null ) {
console.log(el.nodeName);
var sibCount = 0;
var sibIndex = 0;
for ( var i = 0; i < el.parentNode.childNodes.length; i++ ) {
var sib = el.parentNode.childNodes[i];
if ( sib.nodeName == el.nodeName ) {
if ( sib === el ) {
sibIndex = sibCount;
}
sibCount++;
}
}
if ( el.hasAttribute('id') && el.id != '' ) {
stack.unshift(el.nodeName.toLowerCase() + '#' + el.id);
} else if ( sibCount > 1 ) {
stack.unshift(el.nodeName.toLowerCase() + ':eq(' + sibIndex + ')');
} else {
stack.unshift(el.nodeName.toLowerCase());
}
el = el.parentNode;
}
return stack.slice(1); // removes the html element
}
Using jQuery, like this (followed by a solution that doesn't use jQuery except for the event; lots fewer function calls, if that's important):
$(".rightArrow").click(function () {
const rightArrowParents = [];
$(this)
.parents()
.addBack()
.not("html")
.each(function () {
let entry = this.tagName.toLowerCase();
const className = this.className.trim();
if (className) {
entry += "." + className.replace(/ +/g, ".");
}
rightArrowParents.push(entry);
});
console.log(rightArrowParents.join(" "));
return false;
});
Live example:
$(".rightArrow").click(function () {
const rightArrowParents = [];
$(this)
.parents()
.addBack()
.not("html")
.each(function () {
let entry = this.tagName.toLowerCase();
const className = this.className.trim();
if (className) {
entry += "." + className.replace(/ +/g, ".");
}
rightArrowParents.push(entry);
});
console.log(rightArrowParents.join(" "));
return false;
});
<div class=" lol multi ">
Click here
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
(In the live examples, I've updated the class attribute on the div to be lol multi to demonstrate handling multiple classes.)
That uses parents to get the ancestors of the element that was clicked, removes the html element from that via not (since you started at body), then loops through creating entries for each parent and pushing them on an array. Then we use addBack to add the a back into the set, which also changes the order of the set to what you wanted (parents is special, it gives you the parents in the reverse of the order you wanted, but then addBack puts it back in DOM order). Then it uses Array#join to create the space-delimited string.
When creating the entry, we trim className (since leading and trailing spaces are preserved, but meaningless, in the class attribute), and then if there's anything left we replace any series of one or more spaces with a . to support elements that have more than one class (<p class='foo bar'> has className = "foo bar", so that entry ends up being p.foo.bar).
Just for completeness, this is one of those places where jQuery may be overkill, you can readily do this just by walking up the DOM:
$(".rightArrow").click(function () {
const rightArrowParents = [];
for (let elm = this; elm; elm = elm.parentNode) {
let entry = elm.tagName.toLowerCase();
if (entry === "html") {
break;
}
const className = elm.className.trim();
if (className) {
entry += "." + className.replace(/ +/g, ".");
}
rightArrowParents.push(entry);
}
rightArrowParents.reverse();
console.log(rightArrowParents.join(" "));
return false;
});
Live example:
$(".rightArrow").click(function () {
const rightArrowParents = [];
for (let elm = this; elm; elm = elm.parentNode) {
let entry = elm.tagName.toLowerCase();
if (entry === "html") {
break;
}
const className = elm.className.trim();
if (className) {
entry += "." + className.replace(/ +/g, ".");
}
rightArrowParents.push(entry);
}
rightArrowParents.reverse();
console.log(rightArrowParents.join(" "));
return false;
});
<div class=" lol multi ">
Click here
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
There we just use the standard parentNode property (or we could use parentElement) of the element repeatedly to walk up the tree until either we run out of parents or we see the html element. Then we reverse our array (since it's backward to the output you wanted), and join it, and we're good to go.
I needed a native JS version, that returns CSS standard path (not jQuery), and deals with ShadowDOM. This code is a minor update on Michael Connor's answer, just in case someone else needs it:
function getDomPath(el) {
if (!el) {
return;
}
var stack = [];
var isShadow = false;
while (el.parentNode != null) {
// console.log(el.nodeName);
var sibCount = 0;
var sibIndex = 0;
// get sibling indexes
for ( var i = 0; i < el.parentNode.childNodes.length; i++ ) {
var sib = el.parentNode.childNodes[i];
if ( sib.nodeName == el.nodeName ) {
if ( sib === el ) {
sibIndex = sibCount;
}
sibCount++;
}
}
// if ( el.hasAttribute('id') && el.id != '' ) { no id shortcuts, ids are not unique in shadowDom
// stack.unshift(el.nodeName.toLowerCase() + '#' + el.id);
// } else
var nodeName = el.nodeName.toLowerCase();
if (isShadow) {
nodeName += "::shadow";
isShadow = false;
}
if ( sibCount > 1 ) {
stack.unshift(nodeName + ':nth-of-type(' + (sibIndex + 1) + ')');
} else {
stack.unshift(nodeName);
}
el = el.parentNode;
if (el.nodeType === 11) { // for shadow dom, we
isShadow = true;
el = el.host;
}
}
stack.splice(0,1); // removes the html element
return stack.join(' > ');
}
Here is a solution for exact matching of an element.
It is important to understand that the selector (it is not a real one) that the chrome tools show do not uniquely identify an element in the DOM. (for example it will not distinguish between a list of consecutive span elements. there is no positioning/indexing info)
An adaptation from a similar (about xpath) answer
$.fn.fullSelector = function () {
var path = this.parents().addBack();
var quickCss = path.get().map(function (item) {
var self = $(item),
id = item.id ? '#' + item.id : '',
clss = item.classList.length ? item.classList.toString().split(' ').map(function (c) {
return '.' + c;
}).join('') : '',
name = item.nodeName.toLowerCase(),
index = self.siblings(name).length ? ':nth-child(' + (self.index() + 1) + ')' : '';
if (name === 'html' || name === 'body') {
return name;
}
return name + index + id + clss;
}).join(' > ');
return quickCss;
};
And you can use it like this
console.log( $('some-selector').fullSelector() );
Demo at http://jsfiddle.net/gaby/zhnr198y/
The short vanilla ES6 version I ended up using:
Returns the output I'm used to read in Chrome inspector e.g body div.container input#name
function getDomPath(el) {
let nodeName = el.nodeName.toLowerCase();
if (el === document.body) return 'body';
if (el.id) nodeName += '#' + el.id;
else if (el.classList.length)
nodeName += '.' + [...el.classList].join('.');
return getDomPath(el.parentNode) + ' ' + nodeName;
};
I moved the snippet from T.J. Crowder to a tiny jQuery Plugin. I used the jQuery version of him even if he's right that this is totally unnecessary overhead, but i only use it for debugging purpose so i don't care.
Usage:
Html
<html>
<body>
<!-- Two spans, the first will be chosen -->
<div>
<span>Nested span</span>
</div>
<span>Simple span</span>
<!-- Pre element -->
<pre>Pre</pre>
</body>
</html>
Javascript
// result (array): ["body", "div.sampleClass"]
$('span').getDomPath(false)
// result (string): body > div.sampleClass
$('span').getDomPath()
// result (array): ["body", "div#test"]
$('pre').getDomPath(false)
// result (string): body > div#test
$('pre').getDomPath()
Repository
https://bitbucket.org/tehrengruber/jquery.dom.path
I've been using Michael Connor's answer and made a few improvements to it.
Using ES6 syntax
Using nth-of-type instead of nth-child, since nth-of-type looks for children of the same type, rather than any child
Removing the html node in a cleaner way
Ignoring the nodeName of elements with an id
Only showing the path until the closest id, if any. This should make the code a bit more resilient, but I left a comment on which line to remove if you don't want this behavior
Use CSS.escape to handle special characters in IDs and node names
~
export default function getDomPath(el) {
const stack = []
while (el.parentNode !== null) {
let sibCount = 0
let sibIndex = 0
for (let i = 0; i < el.parentNode.childNodes.length; i += 1) {
const sib = el.parentNode.childNodes[i]
if (sib.nodeName === el.nodeName) {
if (sib === el) {
sibIndex = sibCount
break
}
sibCount += 1
}
}
const nodeName = CSS.escape(el.nodeName.toLowerCase())
// Ignore `html` as a parent node
if (nodeName === 'html') break
if (el.hasAttribute('id') && el.id !== '') {
stack.unshift(`#${CSS.escape(el.id)}`)
// Remove this `break` if you want the entire path
break
} else if (sibIndex > 0) {
// :nth-of-type is 1-indexed
stack.unshift(`${nodeName}:nth-of-type(${sibIndex + 1})`)
} else {
stack.unshift(nodeName)
}
el = el.parentNode
}
return stack
}
All the examples from other ответов did not work very correctly for me, I made my own, maybe my version will be more suitable for the rest
const getDomPath = element => {
let templateElement = element
, stack = []
for (;;) {
if (!!templateElement) {
let attrs = ''
for (let i = 0; i < templateElement.attributes.length; i++) {
const name = templateElement.attributes[i].name
if (name === 'class' || name === 'id') {
attrs += `[${name}="${templateElement.getAttribute(name)}"]`
}
}
stack.push(templateElement.tagName.toLowerCase() + attrs)
templateElement = templateElement.parentElement
} else {
break
}
}
return stack.reverse().slice(1).join(' > ')
}
const currentElement = document.querySelectorAll('[class="serp-item__thumb justifier__thumb"]')[7]
const path = getDomPath(currentElement)
console.log(path)
console.log(document.querySelector(path))
console.log(currentElement)
var obj = $('#show-editor-button'),
path = '';
while (typeof obj.prop('tagName') != "undefined"){
if (obj.attr('class')){
path = '.'+obj.attr('class').replace(/\s/g , ".") + path;
}
if (obj.attr('id')){
path = '#'+obj.attr('id') + path;
}
path = ' ' +obj.prop('tagName').toLowerCase() + path;
obj = obj.parent();
}
console.log(path);
hello this function solve the bug related to current element not show in the path
check this now
$j(".wrapper").click(function(event) {
selectedElement=$j(event.target);
var rightArrowParents = [];
$j(event.target).parents().not('html,body').each(function() {
var entry = this.tagName.toLowerCase();
if (this.className) {
entry += "." + this.className.replace(/ /g, '.');
}else if(this.id){
entry += "#" + this.id;
}
entry=replaceAll(entry,'..','.');
rightArrowParents.push(entry);
});
rightArrowParents.reverse();
//if(event.target.nodeName.toLowerCase()=="a" || event.target.nodeName.toLowerCase()=="h1"){
var entry = event.target.nodeName.toLowerCase();
if (event.target.className) {
entry += "." + event.target.className.replace(/ /g, '.');
}else if(event.target.id){
entry += "#" + event.target.id;
}
rightArrowParents.push(entry);
// }
where $j = jQuery Variable
also solve the issue with .. in class name
here is replace function :
function escapeRegExp(str) {
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
}
function replaceAll(str, find, replace) {
return str.replace(new RegExp(escapeRegExp(find), 'g'), replace);
}
Thanks
$(".rightArrow")
.parents()
.map(function () {
var value = this.tagName.toLowerCase();
if (this.className) {
value += '.' + this.className.replace(' ', '.', 'g');
}
return value;
})
.get().reverse().join(", ");

Can not delete an element of array in JavaScript

I have a part of code in which the function removeClass must delete an element of array in case, if some of his elements coincides with input parameters.
But it does not work.
var obj = {
className: 'open menu'
};
function removeClass(obj, cls) {
var arr = obj.className.split(' ');
for (var i = 0; i < arr.Length; i++) {
if (cls == arr[i]) delete arr[i]
}
obj.className = arr.join(' ');
return obj.className;
}
console.log(removeClass(obj, 'open'));
// desired output obj.className='menu'
// actual output 'open menu'
You can use Array.prototype.filter() method for this.
var obj = {
className: 'open menu'
};
function removeClass(obj, cls) {
var arr = obj.className.split(' ');
obj.className = arr.filter(function(item) {
return cls !== item;
}).join(' ')
return obj.className;
}
console.log(removeClass(obj, 'open'));
In your code, you have used arr.Length. Actual syntax is arr.length. But even if you use fix your code then, it will not delete the item instead put undefined on its place, then you have to handle extra white spaces. That's why I think above solution is good.
Try this:
var obj = {
className: 'open menu dogs cats'
};
function removeClass(obj, cls) {
return (obj.className = obj.className.split(' ').filter(item => cls !== item).join(' '));
}
console.log(removeClass(obj, 'open'));
console.log(removeClass(obj, 'dogs'));
But if you are trying to do this to a DOM element then use classList instead.
var el = document.getElementById("mine");
console.log(el.className);
el.classList.remove('open');
console.log(el.className);
el.classList.remove('dogs');
console.log(el.className);
<div id="mine" class="menu open dogs cats"></div>
Why just don't replace the string?
var obj = {
className: 'open menu'
};
function removeClass(obj, cls) {
return obj.className = obj.className.replace(cls, '').trim()
}
console.log(removeClass(obj, 'open')); // obj.className='menu'
Also while working with DOM there are already methods for dooing this
const b = document.querySelector('button')
b.addEventListener('click', () => {
b.classList.remove('blue')
})
.red {
color: red
}
.blue {
color: blue
}
<button class="red blue">test</button>
With ES6 you can even make this really short
function removeClass(element, classToRemove) {
return element.className.split(' ')
.filter(class => class !== classToRemove)
.join(' ');
}
If you want to modify the element itself, just reassign the return value to element.className

SyntaxError: let is a reserved identifier on firefox

I am using those code below
'use strict';
jQuery(document).ready(function($) {
function CMB2ConditionalsInit(context) {
if(typeof context === 'undefined') {
context = 'body';
}
$('[data-conditional-id]', context).each(function(i, e) {
var $e = $(e),
id = $e.data('conditional-id'),
value = $e.data('conditional-value');
var $element = $('[name="' + id + '"]'),
$parent = $e.parents('.cmb-row:first').hide();
$e.data('conditional-required', $e.prop('required')).prop('required', false);
$element
.on('change', function(evt){
let conditionValue = CMB2ConditionalsStringToUnicode(evt.currentTarget.value);
if(typeof value === 'undefined') {
CMB2ConditionalToggleRows('[data-conditional-id="' + id + '"]', ($element.val() ? true : false));
} else {
CMB2ConditionalToggleRows('[data-conditional-id="' + id + '"]:not([data-conditional-value="' + conditionValue + '"])', false);
CMB2ConditionalToggleRows('[data-conditional-id="' + id + '"][data-conditional-value="' + conditionValue + '"]', true);
CMB2ConditionalToggleRows('[data-conditional-id="' + id + '"][data-conditional-value*=\'"' + conditionValue + '"\']', true);
}
});
if($element.length === 1) {
$element.trigger('change');
} else {
$element.filter(':checked').trigger('change');
}
});
}
function CMB2ConditionalsStringToUnicode(string){
let result = '', map = ["Á", "Ă","Ắ","Ặ","Ằ","Ẳ","Ẵ","Ǎ","Â","Ấ","Ậ","Ầ","Ẩ","Ẫ","Ä","Ǟ","Ȧ","Ǡ","Ạ","Ȁ","À","Ả","Ȃ","Ā","Ą","Å","Ǻ","Ḁ","Ⱥ","Ã","Ꜳ","Æ","Ǽ","Ǣ","Ꜵ","Ꜷ","Ꜹ","Ꜻ","Ꜽ","Ḃ","Ḅ","Ɓ","Ḇ","Ƀ","Ƃ","Ć","Č","Ç","Ḉ","Ĉ","Ċ","Ƈ","Ȼ","Ď","Ḑ","Ḓ","Ḋ","Ḍ","Ɗ","Ḏ","Dz","Dž","Đ","Ƌ","DZ","DŽ","É","Ĕ","Ě","Ȩ","Ḝ","Ê","Ế","Ệ","Ề","Ể","Ễ","Ḙ","Ë","Ė","Ẹ","Ȅ","È","Ẻ","Ȇ","Ē","Ḗ","Ḕ","Ę","Ɇ","Ẽ","Ḛ","Ꝫ","Ḟ","Ƒ","Ǵ","Ğ","Ǧ","Ģ","Ĝ","Ġ","Ɠ","Ḡ","Ǥ","Ḫ","Ȟ","Ḩ","Ĥ","Ⱨ","Ḧ","Ḣ","Ḥ","Ħ","Í","Ĭ","Ǐ","Î","Ï","Ḯ","İ","Ị","Ȉ","Ì","Ỉ","Ȋ","Ī","Į","Ɨ","Ĩ","Ḭ","Ꝺ","Ꝼ","Ᵹ","Ꞃ","Ꞅ","Ꞇ","Ꝭ","Ĵ","Ɉ","Ḱ","Ǩ","Ķ","Ⱪ","Ꝃ","Ḳ","Ƙ","Ḵ","Ꝁ","Ꝅ","Ĺ","Ƚ","Ľ","Ļ","Ḽ","Ḷ","Ḹ","Ⱡ","Ꝉ","Ḻ","Ŀ","Ɫ","Lj","Ł","LJ","Ḿ","Ṁ","Ṃ","Ɱ","Ń","Ň","Ņ","Ṋ","Ṅ","Ṇ","Ǹ","Ɲ","Ṉ","Ƞ","Nj","Ñ","NJ","Ó","Ŏ","Ǒ","Ô","Ố","Ộ","Ồ","Ổ","Ỗ","Ö","Ȫ","Ȯ","Ȱ","Ọ","Ő","Ȍ","Ò","Ỏ","Ơ","Ớ","Ợ","Ờ","Ở","Ỡ","Ȏ","Ꝋ","Ꝍ","Ō","Ṓ","Ṑ","Ɵ","Ǫ","Ǭ","Ø","Ǿ","Õ","Ṍ","Ṏ","Ȭ","Ƣ","Ꝏ","Ɛ","Ɔ","Ȣ","Ṕ","Ṗ","Ꝓ","Ƥ","Ꝕ","Ᵽ","Ꝑ","Ꝙ","Ꝗ","Ŕ","Ř","Ŗ","Ṙ","Ṛ","Ṝ","Ȑ","Ȓ","Ṟ","Ɍ","Ɽ","Ꜿ","Ǝ","Ś","Ṥ","Š","Ṧ","Ş","Ŝ","Ș","Ṡ","Ṣ","Ṩ","Ť","Ţ","Ṱ","Ț","Ⱦ","Ṫ","Ṭ","Ƭ","Ṯ","Ʈ","Ŧ","Ɐ","Ꞁ","Ɯ","Ʌ","Ꜩ","Ú","Ŭ","Ǔ","Û","Ṷ","Ü","Ǘ","Ǚ","Ǜ","Ǖ","Ṳ","Ụ","Ű","Ȕ","Ù","Ủ","Ư","Ứ","Ự","Ừ","Ử","Ữ","Ȗ","Ū","Ṻ","Ų","Ů","Ũ","Ṹ","Ṵ","Ꝟ","Ṿ","Ʋ","Ṽ","Ꝡ","Ẃ","Ŵ","Ẅ","Ẇ","Ẉ","Ẁ","Ⱳ","Ẍ","Ẋ","Ý","Ŷ","Ÿ","Ẏ","Ỵ","Ỳ","Ƴ","Ỷ","Ỿ","Ȳ","Ɏ","Ỹ","Ź","Ž","Ẑ","Ⱬ","Ż","Ẓ","Ȥ","Ẕ","Ƶ","IJ","Œ","ᴀ","ᴁ","ʙ","ᴃ","ᴄ","ᴅ","ᴇ","ꜰ","ɢ","ʛ","ʜ","ɪ","ʁ","ᴊ","ᴋ","ʟ","ᴌ","ᴍ","ɴ","ᴏ","ɶ","ᴐ","ᴕ","ᴘ","ʀ","ᴎ","ᴙ","ꜱ","ᴛ","ⱻ","ᴚ","ᴜ","ᴠ","ᴡ","ʏ","ᴢ","á","ă","ắ","ặ","ằ","ẳ","ẵ","ǎ","â","ấ","ậ","ầ","ẩ","ẫ","ä","ǟ","ȧ","ǡ","ạ","ȁ","à","ả","ȃ","ā","ą","ᶏ","ẚ","å","ǻ","ḁ","ⱥ","ã","ꜳ","æ","ǽ","ǣ","ꜵ","ꜷ","ꜹ","ꜻ","ꜽ","ḃ","ḅ","ɓ","ḇ","ᵬ","ᶀ","ƀ","ƃ","ɵ","ć","č","ç","ḉ","ĉ","ɕ","ċ","ƈ","ȼ","ď","ḑ","ḓ","ȡ","ḋ","ḍ","ɗ","ᶑ","ḏ","ᵭ","ᶁ","đ","ɖ","ƌ","ı","ȷ","ɟ","ʄ","dz","dž","é","ĕ","ě","ȩ","ḝ","ê","ế","ệ","ề","ể","ễ","ḙ","ë","ė","ẹ","ȅ","è","ẻ","ȇ","ē","ḗ","ḕ","ⱸ","ę","ᶒ","ɇ","ẽ","ḛ","ꝫ","ḟ","ƒ","ᵮ","ᶂ","ǵ","ğ","ǧ","ģ","ĝ","ġ","ɠ","ḡ","ᶃ","ǥ","ḫ","ȟ","ḩ","ĥ","ⱨ","ḧ","ḣ","ḥ","ɦ","ẖ","ħ","ƕ","í","ĭ","ǐ","î","ï","ḯ","ị","ȉ","ì","ỉ","ȋ","ī","į","ᶖ","ɨ","ĩ","ḭ","ꝺ","ꝼ","ᵹ","ꞃ","ꞅ","ꞇ","ꝭ","ǰ","ĵ","ʝ","ɉ","ḱ","ǩ","ķ","ⱪ","ꝃ","ḳ","ƙ","ḵ","ᶄ","ꝁ","ꝅ","ĺ","ƚ","ɬ","ľ","ļ","ḽ","ȴ","ḷ","ḹ","ⱡ","ꝉ","ḻ","ŀ","ɫ","ᶅ","ɭ","ł","lj","ſ","ẜ","ẛ","ẝ","ḿ","ṁ","ṃ","ɱ","ᵯ","ᶆ","ń","ň","ņ","ṋ","ȵ","ṅ","ṇ","ǹ","ɲ","ṉ","ƞ","ᵰ","ᶇ","ɳ","ñ","nj","ó","ŏ","ǒ","ô","ố","ộ","ồ","ổ","ỗ","ö","ȫ","ȯ","ȱ","ọ","ő","ȍ","ò","ỏ","ơ","ớ","ợ","ờ","ở","ỡ","ȏ","ꝋ","ꝍ","ⱺ","ō","ṓ","ṑ","ǫ","ǭ","ø","ǿ","õ","ṍ","ṏ","ȭ","ƣ","ꝏ","ɛ","ᶓ","ɔ","ᶗ","ȣ","ṕ","ṗ","ꝓ","ƥ","ᵱ","ᶈ","ꝕ","ᵽ","ꝑ","ꝙ","ʠ","ɋ","ꝗ","ŕ","ř","ŗ","ṙ","ṛ","ṝ","ȑ","ɾ","ᵳ","ȓ","ṟ","ɼ","ᵲ","ᶉ","ɍ","ɽ","ↄ","ꜿ","ɘ","ɿ","ś","ṥ","š","ṧ","ş","ŝ","ș","ṡ","ṣ","ṩ","ʂ","ᵴ","ᶊ","ȿ","ɡ","ᴑ","ᴓ","ᴝ","ť","ţ","ṱ","ț","ȶ","ẗ","ⱦ","ṫ","ṭ","ƭ","ṯ","ᵵ","ƫ","ʈ","ŧ","ᵺ","ɐ","ᴂ","ǝ","ᵷ","ɥ","ʮ","ʯ","ᴉ","ʞ","ꞁ","ɯ","ɰ","ᴔ","ɹ","ɻ","ɺ","ⱹ","ʇ","ʌ","ʍ","ʎ","ꜩ","ú","ŭ","ǔ","û","ṷ","ü","ǘ","ǚ","ǜ","ǖ","ṳ","ụ","ű","ȕ","ù","ủ","ư","ứ","ự","ừ","ử","ữ","ȗ","ū","ṻ","ų","ᶙ","ů","ũ","ṹ","ṵ","ᵫ","ꝸ","ⱴ","ꝟ","ṿ","ʋ","ᶌ","ⱱ","ṽ","ꝡ","ẃ","ŵ","ẅ","ẇ","ẉ","ẁ","ⱳ","ẘ","ẍ","ẋ","ᶍ","ý","ŷ","ÿ","ẏ","ỵ","ỳ","ƴ","ỷ","ỿ","ȳ","ẙ","ɏ","ỹ","ź","ž","ẑ","ʑ","ⱬ","ż","ẓ","ȥ","ẕ","ᵶ","ᶎ","ʐ","ƶ","ɀ","ff","ffi","ffl","fi","fl","ij","œ","st","ₐ","ₑ","ᵢ","ⱼ","ₒ","ᵣ","ᵤ","ᵥ","ₓ"];
for(let i = 0; i < string.length; i++){
if(jQuery.inArray(string[i], map) === -1) {
result += string[i]
} else {
result += "\\\\u" + ("000" + string[i].charCodeAt(0).toString(16)).substr(-4);
}
}
return result;
};
function CMB2ConditionalToggleRows(obj, showOrHide){
var $elements = (obj instanceof jQuery) ? obj : $(obj);
return $elements.each(function(i, e) {
let $e = $(e);
$e.prop('required', showOrHide && $e.data('conditional-required'));
$e.parents('.cmb-row:first').toggle(showOrHide);
});
}
CMB2ConditionalsInit('#post');
});
But it is giving me error below
After searched stackoverflow, I got that it will not work on firefox. Is there any way to fix it?
BTW, other browser working fine. Thanks in advance.
As you can see the let keyword isn't supported on FF yet: https://kangax.github.io/compat-table/es6/
You will need to change it to var, or transpile your code with babel

Can this jQuery be done in vanilla JS?

I've got this working on mobile devices, but because of the 32kb gzip-ed of jQuery I wonder if it's possible to create this code
$(document).ready(function() {
$('body').addClass('js');
var $menu = $('#menu'),
$menulink = $('.menu-link'),
$wrap = $('#wrap');
$menulink.click(function() {
$menulink.toggleClass('active');
$wrap.toggleClass('active');
return false;
});
});
can be written in no library dependany vanilla JavaScript.
Can it be done? Where would I start?
JQuery uses javascript/DOMscripting to create its framework. Everything JQuery does, can be done in basic scripting. For example $('body').addClass('js') can be written as:
document.querySelector('body').className += ' js';
And $menulink.toggleClass('active'); as something like
var current = $menulink.className.split(/\s+/)
,toggleClass = 'active'
,exist = ~current.indexOf(toggleClass)
;
current.splice(exist ? current.indexOf(toggleClass) : 0,
exist ? 1 : 0,
exist ? null : toggleClass);
$menulink.className = current.join(' ').replace(/^\s+|\s+$/,'');
That's why JQuery wrapped this kind of code.
This jsfiddle contains a working example using javascript without a framework. Besides that it demonstrates how to program your own element wrapper.
Where to start? You'll have to dive into javascript I suppose. Or check this SO-question
For modern browsers only.®
document.addEventListener('DOMContentLoaded', function() {
document.body.classList.add('js');
var wrap = document.getElementById('wrap');
var menuLinks = Array.prototype.slice.call(document.getElementsByClassName('menu-link'));
var toggleActive = function(element) {
element.classList.toggle('active');
};
menuLinks.forEach(function(menuLink) {
menuLink.addEventListener('click', function(e) {
menuLinks.forEach(toggleActive);
toggleActive(wrap);
}, false);
});
}, false);
var toggleClass = function (el, className) {
if(el) {
if(el.className.indexOf(className)) {
el.className = el.className.replace(className, '');
}
else {
el.className += ' ' + className;
}
}
};
document.addEventListener('DOMContentLoaded', function () {
document.body.className += ' js';
var $menu = document.querySelector('#menu'),
$menulink = document.querySelectorAll('.menu-link'),
$wrap = document.querySelector('#wrap');
$menulink.addEventListener('click', function (e) {
toggleClass($menulink, 'active');
toggleClass($wrap, 'active');
e.preventDefault();
});
});
There's always classList (workaround for incompatible browsers included).
Absolutely. Since jQuery is a subset of JavaScript (written entirely in JavaScript) any function you like can be duplicated. It's a matter of how much effort you want to put into it. Below is how I would duplicate the limited subset of jQuery in your post and it's reasonably cross-browser compatible (if a wee bit long...).
var Vanilla;
if (!Vanilla) {
Vanilla = {};
}
//execute this now to have access to it immediately.
(function () {
'use strict';
Vanilla.addHandler = function (elem, event, handler) {
if (elem.addEventListener) {
elem.addEventListener(event, handler, false);
} else if (elem.attachEvent) {
elem.attachEvent('on' + event, handler);
}
};
Vanilla.hasClass = function (elem, cssClass) {
var classExists = false;
//
if (elem && typeof elem.className === 'string' && (/\S+/g).test(cssClass)) {
classExists = elem.className.indexOf(cssClass) > -1;
}
//
return classExists;
};
Vanilla.addClass = function (elem, cssClass) {
if (elem && typeof elem.className === 'string' && (/\S+/g).test(cssClass)) {
//put spaces on either side of the new class to ensure boundaries are always available
elem.className += ' ' + cssClass + ' ';
}
};
Vanilla.removeClass = function (elem, cssClass) {
if (elem && typeof elem.className === 'string'&& (/\S+/g).test(cssClass)) {
//replace the string with regex
cssClass = new RegExp('\\b' + cssClass + '\\b', 'g');
elem.className = elem.className.replace(cssClass, '').replace(/^\s+/g, '').replace(/\s+$/g, ''); //trim className
}
};
Vanilla.toggleClass = function (elem, cssClass) {
if (Vanilla.hasClass(elem, cssClass)) {
Vanilla.removeClass(elem, cssClass);
} else {
Vanilla.addClass(elem, cssClass);
}
};
Vanilla.getElementsByClassName = function (cssClass) {
var nodeList = [],
classList = [],
allNodes = null,
i = 0,
j = 0;
if (document.getElementsByClassName1) {
//native method exists in browser.
nodeList = document.getElementsByClassName(cssClass);
} else {
//need a custom function
classList = cssClass.split(' ');
allNodes = document.getElementsByTagName('*');
for (i = 0; i < allNodes.length; i += 1) {
for (j = 0; j < classList.length; j += 1) {
if (Vanilla.hasClass(allNodes[i], classList[j])) {
nodeList.push(allNodes[i]);
}
}
}
}
return nodeList;
};
}());
//Now we have a proper window onload
Vanilla.addHandler(window, 'load', function () {
'use strict';
var body = document.body,
menu = document.getElementById('menu'),
menulink = [],
wrap = document.getElementById('wrap'),
i = 0,
menulinkClickHandler = function (e) {
var i = 0;
for (i = 0; i < menulink.length; i += 1) {
Vanilla.toggleClass(menulink[i], 'active');
}
Vanilla.toggleClass(wrap, 'active');
return false;
};
Vanilla.addClass(body, 'js');
menulink = Vanilla.getElementsByClassName('menu-link');
for (i = 0; i < menulink.length; i += 1) {
Vanilla.addHandler(menulink[i], 'click', menulinkClickHandler);
}
});

Get unique selector of element in Jquery

I want to create something like a recorder whichs tracks all actions of a user. For that, i need to identify elements the user interacts with, so that i can refer to these elements in a later session.
Spoken in pseudo-code, i want to be able to do something like the following
Sample HTML (could be of any complexity):
<html>
<body>
<div class="example">
<p>foo</p>
<span>bar</span>
</div>
</body>
</html>
User clicks on something, like the link. Now i need to identify the clicked element and save its location in the DOM tree for later usage:
(any element).onclick(function() {
uniqueSelector = $(this).getUniqueSelector();
})
Now, uniqueSelector should be something like (i don't mind if it is xpath or css selector style):
html > body > div.example > span > a
This would provide the possibility to save that selector string and use it at a later time, to replay the actions the user made.
How is that possible?
Update
Got my answer: Getting a jQuery selector for an element
I'll answer this myself, because i found a solution which i had to modify. The following script is working and is based on a script of Blixt:
jQuery.fn.extend({
getPath: function () {
var path, node = this;
while (node.length) {
var realNode = node[0], name = realNode.name;
if (!name) break;
name = name.toLowerCase();
var parent = node.parent();
var sameTagSiblings = parent.children(name);
if (sameTagSiblings.length > 1) {
var allSiblings = parent.children();
var index = allSiblings.index(realNode) + 1;
if (index > 1) {
name += ':nth-child(' + index + ')';
}
}
path = name + (path ? '>' + path : '');
node = parent;
}
return path;
}
});
Same solution like that one from #Alp but compatible with multiple jQuery elements.
jQuery('.some-selector') can result in one or many DOM elements. #Alp's solution works only with the first one. My solution concatenates all the patches with , if necessary.
jQuery.fn.extend({
getPath: function() {
var pathes = [];
this.each(function(index, element) {
var path, $node = jQuery(element);
while ($node.length) {
var realNode = $node.get(0), name = realNode.localName;
if (!name) { break; }
name = name.toLowerCase();
var parent = $node.parent();
var sameTagSiblings = parent.children(name);
if (sameTagSiblings.length > 1)
{
var allSiblings = parent.children();
var index = allSiblings.index(realNode) + 1;
if (index > 0) {
name += ':nth-child(' + index + ')';
}
}
path = name + (path ? ' > ' + path : '');
$node = parent;
}
pathes.push(path);
});
return pathes.join(',');
}
});
If you want just handle the first element do it like this:
jQuery('.some-selector').first().getPath();
// or
jQuery('.some-selector:first').getPath();
I think a better solution would be to generate a random id and then access an element based on that id:
Assigning unique id:
// or some other id-generating algorithm
$(this).attr('id', new Date().getTime());
Selecting based on the unique id:
// getting unique id
var uniqueId = $(this).getUniqueId();
// or you could just get the id:
var uniqueId = $(this).attr('id');
// selecting by id:
var element = $('#' + uniqueId);
// if you decide to use another attribute other than id:
var element = $('[data-unique-id="' + uniqueId + '"]');
(any element).onclick(function() {
uniqueSelector = $(this).getUniqueSelector();
})
this IS the unique selector and path to that clicked element. Why not use that? You can utilise jquery's $.data() method to set the jquery selector. Alternatively just push the elements you need to use in the future:
var elements = [];
(any element).onclick(function() {
elements.push(this);
})
If you really need the xpath, you can calculate it using the following code:
function getXPath(node, path) {
path = path || [];
if(node.parentNode) {
path = getXPath(node.parentNode, path);
}
if(node.previousSibling) {
var count = 1;
var sibling = node.previousSibling
do {
if(sibling.nodeType == 1 && sibling.nodeName == node.nodeName) {count++;}
sibling = sibling.previousSibling;
} while(sibling);
if(count == 1) {count = null;}
} else if(node.nextSibling) {
var sibling = node.nextSibling;
do {
if(sibling.nodeType == 1 && sibling.nodeName == node.nodeName) {
var count = 1;
sibling = null;
} else {
var count = null;
sibling = sibling.previousSibling;
}
} while(sibling);
}
if(node.nodeType == 1) {
path.push(node.nodeName.toLowerCase() + (node.id ? "[#id='"+node.id+"']" : count > 0 ? "["+count+"]" : ''));
}
return path;
};
Reference: http://snippets.dzone.com/posts/show/4349
Pure JavaScript Solution
Note: This uses Array.from and Array.prototype.filter, both of which need to be polyfilled in IE11.
function getUniqueSelector(node) {
let selector = "";
while (node.parentElement) {
const siblings = Array.from(node.parentElement.children).filter(
e => e.tagName === node.tagName
);
selector =
(siblings.indexOf(node)
? `${node.tagName}:nth-of-type(${siblings.indexOf(node) + 1})`
: `${node.tagName}`) + `${selector ? " > " : ""}${selector}`;
node = node.parentElement;
}
return `html > ${selector.toLowerCase()}`;
}
Usage
getUniqueSelector(document.getElementsByClassName('SectionFour')[0]);
getUniqueSelector(document.getElementById('content'));
While the question was for jQuery, in ES6, it is pretty easy to get something similar to #Alp's for Vanilla JavaScript (I've also added a couple lines, tracking a nameCount, to minimize use of nth-child):
function getSelectorForElement (elem) {
let path;
while (elem) {
let subSelector = elem.localName;
if (!subSelector) {
break;
}
subSelector = subSelector.toLowerCase();
const parent = elem.parentElement;
if (parent) {
const sameTagSiblings = parent.children;
if (sameTagSiblings.length > 1) {
let nameCount = 0;
const index = [...sameTagSiblings].findIndex((child) => {
if (elem.localName === child.localName) {
nameCount++;
}
return child === elem;
}) + 1;
if (index > 1 && nameCount > 1) {
subSelector += ':nth-child(' + index + ')';
}
}
}
path = subSelector + (path ? '>' + path : '');
elem = parent;
}
return path;
}
I found for my self some modified solution. I added to path selector #id, .className and cut the lenght of path to #id:
$.fn.extend({
getSelectorPath: function () {
var path,
node = this,
realNode,
name,
parent,
index,
sameTagSiblings,
allSiblings,
className,
classSelector,
nestingLevel = true;
while (node.length && nestingLevel) {
realNode = node[0];
name = realNode.localName;
if (!name) break;
name = name.toLowerCase();
parent = node.parent();
sameTagSiblings = parent.children(name);
if (realNode.id) {
name += "#" + node[0].id;
nestingLevel = false;
} else if (realNode.className.length) {
className = realNode.className.split(' ');
classSelector = '';
className.forEach(function (item) {
classSelector += '.' + item;
});
name += classSelector;
} else if (sameTagSiblings.length > 1) {
allSiblings = parent.children();
index = allSiblings.index(realNode) + 1;
if (index > 1) {
name += ':nth-child(' + index + ')';
}
}
path = name + (path ? '>' + path : '');
node = parent;
}
return path;
}
});
This answer does not satisfy the original question description, however it does answer the title question. I came to this question looking for a way to get a unique selector for an element but I didn't have a need for the selector to be valid between page-loads. So, my answer will not work between page-loads.
I feel like modifying the DOM is not idel, but it is a good way to build a selector that is unique without a tun of code. I got this idea after reading #Eli's answer:
Assign a custom attribute with a unique value.
$(element).attr('secondary_id', new Date().getTime())
var secondary_id = $(element).attr('secondary_id');
Then use that unique id to build a CSS Selector.
var selector = '[secondary_id='+secondary_id+']';
Then you have a selector that will select your element.
var found_again = $(selector);
And you many want to check to make sure there isn't already a secondary_id attribute on the element.
if ($(element).attr('secondary_id')) {
$(element).attr('secondary_id', (new Date()).getTime());
}
var secondary_id = $(element).attr('secondary_id');
Putting it all together
$.fn.getSelector = function(){
var e = $(this);
// the `id` attribute *should* be unique.
if (e.attr('id')) { return '#'+e.attr('id') }
if (e.attr('secondary_id')) {
return '[secondary_id='+e.attr('secondary_id')+']'
}
$(element).attr('secondary_id', (new Date()).getTime());
return '[secondary_id='+e.attr('secondary_id')+']'
};
var selector = $('*').first().getSelector();
In case you have an identity attribute (for example id="something"), you should get the value of it like,
var selector = "[id='" + $(yourObject).attr("id") + "']";
console.log(selector); //=> [id='something']
console.log($(selector).length); //=> 1
In case you do not have an identity attribute and you want to get the selector of it, you can create an identity attribute. Something like the above,
var uuid = guid();
$(yourObject).attr("id", uuid); // Set the uuid as id of your object.
You can use your own guid method, or use the source code found in this so answer,
function guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
}
My Vanilla JavaScript function:
function getUniqueSelector( element ) {
if (element.id) {
return '#' + element.id;
} else if (element.tagName === 'BODY') {
return 'BODY';
} else {
return `${getUniqueSelector(element.parentElement)} > ${element.tagName}:nth-child(${myIndexOf(element)})`;
}
}
function myIndexOf( element ) {
let index = 1;
// noinspection JSAssignmentUsedAsCondition
while (element = element.previousElementSibling) index++;
return index;
}
You may also have a look at findCssSelector. Code is in my other answer.
You could do something like this:
$(".track").click(function() {
recordEvent($(this).attr("id"));
});
It attaches an onclick event handler to every object with the track class. Each time an object is clicked, its id is fed into the recordEvent() function. You could make this function record the time and id of each object or whatever you want.
$(document).ready(function() {
$("*").click(function(e) {
var path = [];
$.each($(this).parents(), function(index, value) {
var id = $(value).attr("id");
var class = $(value).attr("class");
var element = $(value).get(0).tagName
path.push(element + (id.length > 0 ? " #" + id : (class.length > 0 ? " .": "") + class));
});
console.log(path.reverse().join(">"));
return false;
});
});
Working example: http://jsfiddle.net/peeter/YRmr5/
You'll probably run into issues when using the * selector (very slow) and stopping the event from bubbling up, but cannot really help there without more HTML code.
You could do something like that (untested)
function GetPathToElement(jElem)
{
var tmpParent = jElem;
var result = '';
while(tmpParent != null)
{
var tagName = tmpParent.get().tagName;
var className = tmpParent.get().className;
var id = tmpParent.get().id;
if( id != '') result = '#' + id + result;
if( className !='') result = '.' + className + result;
result = '>' + tagName + result;
tmpParent = tmpParent.parent();
}
return result;
}
this function will save the "path" to the element, now to find the element again in the future it's gonna be nearly impossible the way html is because in this function i don't save the sibbling index of each element,i only save the id(s) and classes.
So unless each and every-element of your html document have an ID this approach won't work.
Getting the dom path using jquery and typescript functional programming
function elementDomPath( element: any, selectMany: boolean, depth: number ) {
const elementType = element.get(0).tagName.toLowerCase();
if (elementType === 'body') {
return '';
}
const id = element.attr('id');
const className = element.attr('class');
const name = elementType + ((id && `#${id}`) || (className && `.${className.split(' ').filter((a: any) => a.trim().length)[0]}`) || '');
const parent = elementType === 'html' ? undefined : element.parent();
const index = (id || !parent || selectMany) ? '' : ':nth-child(' + (Array.from(element[0].parentNode.children).indexOf(element[0]) + 1) + ')';
return !parent ? 'html' : (
elementDomPath(parent, selectMany, depth + 1) +
' ' +
name +
index
);
}
Pass the js element (node) to this function.. working little bit..
try and post your comments
function getTargetElement_cleanSelector(element){
let returnCssSelector = '';
if(element != undefined){
returnCssSelector += element.tagName //.toLowerCase()
if(element.className != ''){
returnCssSelector += ('.'+ element.className.split(' ').join('.'))
}
if(element.id != ''){
returnCssSelector += ( '#' + element.id )
}
if(document.querySelectorAll(returnCssSelector).length == 1){
return returnCssSelector;
}
if(element.name != undefined && element.name.length > 0){
returnCssSelector += ( '[name="'+ element.name +'"]' )
}
if(document.querySelectorAll(returnCssSelector).length == 1){
return returnCssSelector;
}
console.log(returnCssSelector)
let current_parent = element.parentNode;
let unique_selector_for_parent = getTargetElement_cleanSelector(current_parent);
returnCssSelector = ( unique_selector_for_parent + ' > ' + returnCssSelector )
console.log(returnCssSelector)
if(document.querySelectorAll(returnCssSelector).length == 1){
return returnCssSelector;
}
}
return returnCssSelector;
}

Categories

Resources