.attr("href") returning undefined - javascript

Here is a simplified version of my HTML
<div class="post-title-div">
Link
</div>
<div></div>
<div>
<img class="eye-icon" src="link.jpg"/>
</div>
JavaScript:
$(".eye-icon").click(function(){
var link = $(this).parent('.post-title-div').find(".post-title").attr("href");
})
alert(link) returns undefined, why?
$(this).parent('.post-title-div').find(".post-title") by itself returns [object Object]

Parent/Parents/Closest is not what you're after :
$(".eye-icon").click(function(){
var link = $(this).parent().prev().prev().find(".post-title").attr("href");
alert(link)
})
You don't have a common parent there.
http://jsbin.com/tupeta/edit?html,js,output
However , I suggest that you add a new container which will contain them all . something like :
<div class='wrapper'>
<div class="post-title-div">
Link
</div>
<div></div>
<div>
<img class="eye-icon" src="link.jpg"/>
</div>
</div>
So now you can do :
$(".eye-icon").click(function(){
var link = $(this).closest('.wrapper').find(".post-title").attr("href");
alert(link)
})

The parent of the <img> is the nondescript <div> which surrounds it. jQuery then looks in that div for '.post-title-div', doesn't find anything and returns an empty selector object. In that empty selector it then looks for .post-title and - naturally - doesn't find anything either. It then tries to read an attribute on the new, also empty, selector, which of course can't return anything useful.
The reason why $(this).parent('.post-title-div').find(".post-title") returns an [object Object] is because jQuery selectors never return undefined, they always return another selector, which can be an empty selector.

Related

Why does my document.getElementsByClassName("obj").innerHTML doesn't work?

I want to replace HTML in document with .innerHTML but for some reason it doesn't work:
HTML
<div class="specs">
<div class="wrapp">
<p class="line">Content</p>
</div>
</div>
JS
document.getElementsByClassName("specs").innerHTML = "<p>Lorem ipsum</p>";
getElementsByClassName returns a collection. Not a single item.
There are multiple ways to do this:
You can run a for loop over the returned items.
let specs = document.getElementsByClassName("specs");
for(let i = 0 ; i < specs.length;i++){
specs[i].innerHTML = "<p>Lorem ipsum</p>";
}
If you have only item, you can use querySelector which returns the first matched element.
document.querySelector(".specs").innerHTML = "<p>Lorem ipsum</p>";
In a concise way this is how you'd do it
const targets = document.getElementsByClassName("specs")
if (targets.length)
for (spec in targets)
targets[spec].innerHTML = "<p>Lorem ipsum</p>";
<div class="specs">
<div class="wrapp">
<p class="line">Content</p>
</div>
</div>
I found your mistake.
document.getElementsByClassName returns an array of elements with the given class name. so try this.
document.getElementsByClassName("specs")[0].innerHTML = "<p>Lorem ipsum</p>";
For example if you have two elements with the same class name it returns an array containing both the elements, so you have to get the element using the specified index from the array. 👍

change style if a element exist in javascript

I would like to change the style, if there is a specific class on the page,
but it doesn't work. what is wrong with below code snippet?
https://jsfiddle.net/1wc0xdor/
<html>
<body>
<div id='category'>
<div id="search_filters_wrapper">
Filter
</div>
<div class="st_banner_row" style="">
There is Banner
</div>
</div>
</body>
</html>
<script>
var elementExists = document.getElementById('category').getElementsByClassName('st_banner_row');
if (typeof(elementExists) != 'undefined' && elementExists != null)
{
$("#search_filters_wrapper").css({
margin-top: 40,
});
}
</script>
document.getElementsByClassName returns a NodeList. If the class isn't found, it will return an empty list, not null.
Check the length rather than whether it's null.
Also, margin-top is not a valid identifier, you need to quote it to use it as an object key (or you can change to camelCase marginTop:)
if (elementExists.length != 0) {
$("#search_filters_wrapper").css({
"margin-top": 40,
});
}
getElementsByClassName always returns a NodeList (think, array), so elementExists always... exists. So you really only need to check if the array isn't empty to be sure that your target class exists. Further, rather than calling getElementById first, you really only need to call getElementsByClassName, unless you're specifically looking for this class within the scope of the parent element with that id.
If you do need to search within the scope of the parent element with that id, consider using querySelectorAll with an appropriate CSS selector
const elements = document.querySelectorAll('#category .st_banner_row');
if (elements.length) {
$('#search_filters_wrapper').css({
'margin-top': 40
});
}
Also, consider setting a CSS class here rather than programmatically setting the css attribute directly, as the latter is bad practice unless it can't be helped.
Answer:
When you check if the element exists you are actually looking at an Array. To determine if the Array is not empty, and therefore, the class exists within the category element, you just need to test the length property.
If the Array length is 0 and nothing is in it, it will return false. If the Array length is greater than 0, something is in it, and it will return true.
Secondly when you utilize properties that have a hyphen( - ) you need to pass that property as a String within the object you're passing to the css method of JQuery's element wrapper.
var elementExists = document.getElementById('category')
.getElementsByClassName('st_banner_row')
.length;
if (elementExists)
{
$("#search_filters_wrapper").css({"margin-top": 40});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id='category'>
<span>Below is 40px</span>
<div id="search_filters_wrapper">
Filter
</div>
<div class="st_banner_row" style="">
There is Banner
</div>
</div>
Aside:
It's odd that you're using JQuery for this one aspect of code. It would be easier and more maintainable to use either all Vanilla JavaScript or all JQuery.
JQuery:
var elementExists = $("#category").find(".st_banner_row").length;
if (elementExists)
{
$("#search_filters_wrapper").css({"margin-top": 40});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id='category'>
<span>Below is 40px</span>
<div id="search_filters_wrapper">
Filter
</div>
<div class="st_banner_row" style="">
There is Banner
</div>
</div>
JavaScript:
var elementExists = document.getElementById('category')
.getElementsByClassName('st_banner_row')
.length;
if (elementExists)
{
document.getElementById("search_filters_wrapper").style.marginTop = "40px";
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id='category'>
<span>Below is 40px</span>
<div id="search_filters_wrapper">
Filter
</div>
<div class="st_banner_row" style="">
There is Banner
</div>
</div>
Alternative Vanilla JS using querySelector:
var elementExists = document.querySelector('#category .st_banner_row');
if (elementExists)
{
document.querySelector("#search_filters_wrapper").style.marginTop = "40px";
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id='category'>
<span>Below is 40px</span>
<div id="search_filters_wrapper">
Filter
</div>
<div class="st_banner_row" style="">
There is Banner
</div>
</div>
Note In this version we don't need to check the length because if category does not have a child with a class of st_banner_row the selector will return undefined.
Alternative Vanilla JavaScript Functional example:
// helper functions
const el = ( query, context = document ) => context.querySelector( query ),
elementExists = query => Boolean( el( query ) ),
ifElementExists = ( query, fn = () => undefined ) => elementExists( query ) && fn(),
elStyle = query => ( prop, value ) => el( query ).style[ prop ] = value,
changeStyle = query => ( prop, value ) => () => elStyle( query )( prop, value );
// execution
ifElementExists( "#category .st_banner_row",
changeStyle( "#search_filters_wrapper" )( "margin-top", "40px" )
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id='category'>
<span>Below is 40px</span>
<div id="search_filters_wrapper">
Filter
</div>
<div class="st_banner_row" style="">
There is Banner
</div>
</div>

Create list from each

I am looping through elements using jQuery like this:
$(".myelement").each(function() {
$element = $(this).closest(".panel").attr("id");
console.log($element);
});
This is working correctly and I am seeing each of the elements it finds in my console log. I am now trying to get a string containing each element that looks like this:
#element1, #element2, #element3
What is the easiest way to do this? Does anybody have an example they can point me at?
You could use map() to build an array of the id then join() it, something like this:
var ids = $(".myelement").map(function() {
return '#' + $(this).closest(".panel").prop("id");
}).get().join(', ');
console.log(ids);
You could use an array to store them by adding the # in every iteration, then after the loop end join them using join() method like :
var ids = [];
$(".myelement").each(function() {
ids.push('#' + $(this).closest(".panel").attr("id"));
});
console.log(ids.join(', '));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="element1" class="panel">
<span class="myelement">My element 1</span>
</div>
<div id="element2" class="panel">
<span class="myelement">My element 2</span>
</div>
<div id="element3" class="panel">
<span class="myelement">My element 3</span>
</div>
Try with map()
The .map() method is particularly useful for getting or setting the value of a collection of elements.
As the return value is a jQuery object, which contains an array, it's very common to call .get() on the result to work with a basic array.
You can use map(), get() and join()
in the following way:
var $element = $(".myelement").map(function(){
return $(this).closest(".panel").attr("id");
}).get().join(', ');
console.log($element);

Acquiring the JSON value of a data attribute, and search for matching Json key -> data attribute in a specific DOM element

I have an HTML element with a data attribute:
Remove
<div data-container>
<p>lorem ipsum<p>
<p data-rem></p>
</div>
1.
in the data-trigger sometimes(not always) I send a value:
I want to gather all elements that have the attribute data-trigger:
document.querySelectorAll(['data-trigger']).forEach(function (trigger, index) {
and for each of this trigger to get the DOM and JSON value and parse it:
dom = trigger
value = JSON.parse(trigger.getAttribute('data-trigger'));
I get the DOM reference but for the value I always get null
By using getAttribute do I call again in the DOM ?
Looping thru data-container search for elements which have the attributes one of the keys found in JSON.parse and set their value, the value of the key.
For example:
<p data-rem>albatros</p>
What is currently stopping your script in its tracks is the selector inside querySelectorAll().
It should be '[data-trigger]', not ['data-trigger'].
After you fix it, you'll be able to get all elements with a data-trigger attribute. However, the string you placed in data-trigger attribute won't parse as a JavaScript object (as you probably expect). Your best bet would be to replace quotes with double quotes and double quotes with quotes, making it a valid JSON string and parse it:
<a href="#" data-trigger='{"rem": "albatros"}'>Remove</a>
JSON.parse(elem.getAttribute('data-trigger')) // where elem is the DOM element above
You haven't been exactly clear on what the purpose of this script is but, assuming you want to find a list of DOM elements having 'data-'+foundKey equal to foundVal, you could make the following selector:
document.querySelectorAll('[data-'+foundKey+'="'+foundVal+'"]').forEach(function(el, i){
console.log(el, i);
})
... but in your case that wouldn't do anything, because you don't have any element with
<x data-rem="albatros"></x>
Here's a working example of what I'm talking about, applied to your markup:
document.querySelectorAll('[data-trigger]').forEach(function (trigger, index) {
const triggerProp = JSON.parse(trigger.getAttribute('data-trigger')),
foundKey = Object.keys(triggerProp)[0],
foundVal = triggerProp[Object.keys(triggerProp)[0]],
targets = document.querySelectorAll('[data-'+foundKey+']').forEach(function(target, index) {
console.log(target, 'key:'+foundKey, 'val:'+target.getAttribute('data-'+foundKey));
});
})
<a href="#" data-trigger='{"rem": "albatros"}'>Remove</a>
<div data-container>
<p>lorem ipsum<p>
<p data-rem="test"></p>
</div>
Do note that it only works because the value of data-trigger is valid JSON. If it were not, it would fail miserably. You could guard your script from this risk by a try/catch function that would test the value as JSON first. Another alternative is to use a library that parses relaxed JSON.
This is not valid JSON String
Remove
Try:
<a href="#" data-trigger='{"rem": "albatros"}'>Remove</a> <!-- Double quotes in JSON -->
Then:
document.querySelectorAll("[data-trigger]").forEach(function (trigger, index) {
var val = JSON.parse(trigger.getAttribute('data-trigger'));
for( key in val){ //Loop through (JSON-)Object
setInnerElements(val[key], key);
}
});
function setInnerElements(val,key){
document.querySelectorAll("[data-container] > [data-"+ key +"]").forEach(function (el, index) {
el.innerHTML = val;
})
}
<a href="#" data-trigger='{"rem": "albatros", "foo":"test1", "bar":"test2"}'>Remove</a>
<div data-container>
<p>lorem ipsum<p>
<p data-rem></p>
<p>lorem ipsum<p>
<p data-foo></p>
<p>lorem ipsum<p>
<p data-bar></p>
</div>

Get value of data attribute using jQuery in parent element

I have the following HTML code where onclick of .spot-info I want to get the value of data-test.
<div class="spot" id="spot-1" data-test="12345">
<div class="spot-main">
<span>Test</span>
view
</div>
</div>
<div class="spot" id="spot-2" data-test="67891">
<div class="spot-main">
<span>Test</span>
view
</div>
</div>
Please see my js attempt below:
$('.spot-info').click(function ( e ) {
e.preventDefault();
var test = $(this).parent().parent().data('data-test');
console.log(test);
});
This test is logging undefined in the console. I thought this would work checking the parent of the parent of .spot-info.
Also, is there way chaining a lot of parent(0) methods together rather than using jQuery.
try attr instead. Also, try 'closest' instead of referring to the parent twice:
$('.spot-info').click(function ( e ) {
e.preventDefault();
var test = $(this).closest('.spot').attr('data-test');
console.log(test);
});
Better if you could use closest() or parents() ,so instead of :
$(this).parent().parent().data('test');
Use :
$(this).closest('.spot').data('test');
$(this).parents('.spot').data('test');
NOTE : The main problem come from .data('data-test'); When you use data() function you shouldn't add the string data- at the start.
Hopet his helps.
Working snippet
$('.spot-info').click(function ( e ) {
e.preventDefault();
var test = $(this).closest('.spot').data('test');
console.log(test);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="spot" id="spot-1" data-test="12345">
<div class="spot-main">
<span>Test</span>
view
</div>
</div>
<div class="spot" id="spot-2" data-test="67891">
<div class="spot-main">
<span>Test</span>
view
</div>
</div>
Not sure - but I think it's this:
$('.spot-info').click(function ( e ) {
e.preventDefault();
var test = $(this).parent().parent().data('test');
console.log(test);
});
You don't need the data- prefix when fetching the data with .data()

Categories

Resources