Polymer querySelector can't find element in dom-repeat - javascript

Goal:
I'm trying to select a div inside a dom-repeat element.
The code below contains the div I want to select. id="med[[index]]"
Code:
<div class="card">
<h1>Medicijn overzicht:</h1>
<template is="dom-repeat" items="{{employees}}">
<div class="inner-card" id="[[index]]">
<div><span><b>{{item.first}}</b></span> <span>{{item.last}}</span>mg<br><span>{{item.stuks}}</span> stuks</div>
<div id="med[[index]]" style="display:none"><br><br><br><br><br>Goed verhaal</div>
</div>
</template>
</div>
Problem:
The goal here is to have the div expand when the parent gets clicked, but the querySelect seems to be unable to find the element when it is called from within the click function. When preforming this search at the top of the function with a static name it finds it just fine.
attached:function(){
this.async(function() {
var cards = Polymer.dom(this.root).querySelectorAll(".inner-card");
var test = Polymer.dom(this.root).querySelector("#med0"); //Finds the element
console.log(test); // This is fine
console.log("cards",cards);
$(cards).click(function(evt){
var target= evt.currentTarget;
var tindex = target.id;
var targetDiv = Polymer.dom(this.root).querySelector("#med"+tindex); //Doesn't find it.
console.log("#med"+tindex)
console.log(targetDiv);
$(targetDiv).slideDown("slow");
console.log("trigger"+tindex);
});
});
}
It might just be me misunderstanding how this works, I'm very new to this.
Can anyone tell me what I'm doing wrong here?
Thanks in advance.

Another solution would be to add .bind(this) after end of function. I am using it in old projects, where ES6 can't be used. Using .bind(this) seems more 'cleaner'
In your case:
$(cards).click(function(evt){
var target= evt.currentTarget;
var tindex = target.id;
var targetDiv = Polymer.dom(this.root).querySelector("#med"+tindex); //Doesn't find it.
console.log("#med"+tindex)
console.log(targetDiv);
$(targetDiv).slideDown("slow");
console.log("trigger"+tindex);
}.bind(this));

The reference to this.root within click-handler is different than the one referenced above it. root probably does not exist within the this context of the click-handler—it certainly is not the same as the this.root above, since that this is referring to the global this.
You might want to save this or this.root for use within the click handler:
this.async(function() {
var cards = Polymer.dom(this.root).querySelectorAll(".inner-card");
var test = Polymer.dom(this.root).querySelector("#med0");
var root_root=this.root; // **ADD THIS**
$(cards).click(function(evt){
var target= evt.currentTarget;
var tindex = target.id;
var targetDiv = Polymer.dom(root_root).querySelector("#med"+tindex); // **CHANGE THIS**
$(targetDiv).slideDown("slow");
console.log("trigger"+tindex);
});
});
Alternatively, if you can use ES6's arrow functions, you can fix this easier by simply converting the click handler to an arrow function,
$(cards).click((evt) => { …
This will use the this from the surrounding context of the arrow-function.

Related

How to insert multi values to data-tag

I made a jquery filter function, that filtering the results by data-tags. like this:
<div class="resultblock" data-tag="ios">
<img src="images/osx.jpg" class="itemimg">
<div class="desc">
<div class="desc_text">
lorem ipsum
</div>
</div>
i just want to insert in the data-tag another tags to filter. like this:
data-tag="ios,android,windows"
How can i do that?
I am not sure I fully understand the question you are asking, but I think you could accomplish this via JS.
In your html add a script tag and then you just write some JS to edit or add html tags. Here is an example:
<script>
var para = document.createElement("p");
var node = document.createTextNode("This is new.");
para.appendChild(node);
var element = document.getElementById("div1");
element.appendChild(para);
</script>
Now to sort the data-tag:
just add this code to your HTML file.
<div id="div1">
</div>
<script>
var tag ="ios,android,windows"; //initialize variable
var data = tag.split(","); //this makes an array of ios,andrid,windows
var i = 0;
while (i < 3){
alert(i);
var para = document.createElement("p");
var node = document.createTextNode(data[i]);
para.appendChild(node);
var element = document.getElementById("div1");
element.appendChild(para);
i++;
}
</script>
The best way doing this is to use classes. Adding classes and removing them is much easier than other attributes. The classes should not overlap with other classes used for CSS for example. Adding a prefix to them is even better. Like this:
$(".filter-ios").hide(); // hide all ios elements
$("something").addClass("filter-windows"); // add the class windows to an element
$(".filter-ios").addClass("filter-apple"): // add the apple filter class to the ios filter class elements
$("something").hasClass("filter-samsung"); // check if an element has the filter class samsung
// ...
The classes .filter-* should be used for filtering only, they must not have any CSS attached to them, if there is already classes like that, then just change the prefix filter to something else!
I've just created a little object with two methods .add and .remove. It works like classList DOM method for adding and removing classes. If you add one value twice, it's added only once, also if you remove some not existing class, any error will occure. Hope you'll find it helpful.
var el = document.getElementById('myElem');
multiValues = {
add: function(elem,val){
if(elem.constructor.toString().search('HTML')===-1) return;
if(typeof val !=='string') return;
if(!elem.attributes['data-tag']) elem.setAttribute('data-tag');
var attr = elem.attributes['data-tag'];
var parsed = attr.value.split(',');
var isExist = parsed.some(function(a){
return a === val;
});
if(!isExist) parsed.push(val);
elem.setAttribute('data-tag',parsed.join(','));
},
remove: function(elem,val){
if(elem.constructor.toString().search('HTML')===-1) return;
if(typeof val !=='string') return;
if(!elem.attributes['data-tag']) return;
var attr = elem.attributes['data-tag'];
var parsed = attr.value.split(',');
parsed.some(function(a,b){
if(a===val){
parsed.splice(b,1);
}
elem.setAttribute('data-tag',parsed.join(','));
});
}
};
multiValues.add(el,'window');
multiValues.add(el,'window');
multiValues.add(el,'window');
multiValues.add(el,'android');
multiValues.remove(el,'a');
multiValues.remove(el,'b');
multiValues.add(el,'something');
console.log(el.attributes['data-tag'].value);
<div class="resultblock" data-tag="ios" id="myElem"></div>

Issue with addEventListener & target clic in my website

I'm playing around with HTML, CSS & JavaScript but I'm not very good. I'm trying the following:
<script type="text/javascript">
var tab = document.getElementsByClassName("MYCLASS");
for(var i = 0, j=tab.length; i<j; i++){
tab[i].addEventListener('click', afficher,false);
}
function afficher(){
alert(this.class);
}
</script>
Attaching the click listener on all my .MYCLASS divs is working. However, on Google Chrome in the alert window it throws me undefined instead of .MYCLASS.
So I tried this code as alternative:
function afficher(e){
var target = e.target || e.srcElement;
var $target = $(e.currentTarget);
alert(target.class);
}
But the result is exactly the same. Any help is appreciated, thank you!
This is because there's no property defined for a html DOM object named class.
If you want to get the class value, you should use this.className.
So your function should look like this:
function afficher(){
alert(this.className);
}
You can just use a simple 'for in' loop, but you need to be sure that you are appending events to only DOM elements. The HTML list that is returned by getElementsByClassName() isn't only returning DOM elements.
var tab = document.getElementsByClassName("MYCLASS");
for(var i in tab){
// you need this check to filter anything that isn't a DOM element
if(typeof tab[i] ==="object"){
tab[i].addEventListener('click', afficher,false);
}
}
function afficher(){
alert(this.className);
}

Javascript - remove button and parent

Hi I am just learning Javascript and after following some tutorials I thought it would be nice to practise some Javascript by making stuff.
So now I am trying to make a very easy to-do-list. Just for practise, but I get stuck.
I managed to add items with a remove-button to an UL with JS. But, BUT:
How do I make it so; when you click on the removeMe button, that only that Li will be removed?
What should I use?
Here's my code:
var buttonAdd = document.getElementById('but1');
var buttonRemove = document.getElementById('but2');
var ul = document.getElementById('myUl');
function addLi() {
var newLi = document.createElement('li');
var removeThis = document.createElement('button');
var textInput = document.getElementById('inputText').value;
if(textInput === ""){
alert('Add text');
}else{
newLi.innerHTML = textInput;
newLi.appendChild(removeThis);
removeThis.innerHTML = "Remove me";
removeThis.setAttribute("onClick", "removeMe(this);");
ul.appendChild(newLi);
}
}
buttonAdd.onclick = function() {
addLi();
};
buttonRemove.onclick = function() {
ul.innerHTML = "";
};
function removeMe(item){
//get called when clicked on the remove button
}
and my HTML:
<body>
<ul id="myUl"></ul>
<input id="inputText" type="text"><br />
<button id="but1">Add stuff</button><br />
<button id="but2">Remove all</button>
</body>
Thanks
The function remove() is a brand new DOM 4 method and not very widely supported yet. The clunky, but bulletproof way would be:
function removeMe(item){
item.parentElement.parentElement.removeChild(item.parentElement);
}
or with a bit more elegance:
function removeMe(item){
var parent = item.parentElement;
parent.parentElement.removeChild(parent);
}
http://jsfiddle.net/BtbR4/
Also be careful with this:
removeThis.setAttribute("onClick", "removeMe(this);");
Handing a function reference as a string is always a bad idea for several reasons (eval'ing the string, messing up the scope). There are several better options:
removeThis.onclick = removeMe;
or if you need to hand over parameters
removeThis.onclick = function(){removeMe(your,parameters)};
The best option however is to attach eventhandlers always like this:
Element.addEventListener("type-of-event",functionReference);
You just need to remove the parent node (the li), as I've shown using jsbin.
function removeMe(item){
item.parentNode.remove();
}
Please note Blue Skies's comment that this may not work across all browsers, an alternative is:
var par = item.parentNode; par.parentNode.removeChild(par);
a cleaner way to do things is to add
removeThis.onclick = removeMe;
and
function removeMe(mouseEvent){
this.parentNode.remove();
}
This is consistent with how you add the other onclick functions in your code. Since you said you are learning js, it is a good idea to learn how events and functions work. So, the take away from this is that the 'this' of a function that is attached to an object is the object itself (the removeThis object in this case), and event handlers give you access to the event that invoked them (mouseEvent) in the argument list.
JSfiddle: http://jsfiddle.net/QT4E3/

How to improve jQuery code

I have the syntax below. I was wondering if this part: $($(this).parent().siblings('div')[0]) could be writen more elegantly by accessing a jQuery object directly without the need to use $(); again.
I used .parent().siblings and didn't use the class of div because I want to reuse the code somewhere where the classes are different.
$('textarea').click(function(){
$($(this).parent().siblings('div')[0]).html('<span>140</span>');
$($(this).parent().siblings('div')[1]).html('<span>Reply</span>');
});
<div class="post_area2">
<div class="wrap_area2 left">
<textarea></textarea>
</div>
<div class="word_c left"></div>
<div class="submit left"></div>
</div>
You can use .eq() method.
$('textarea').click(function(){
$(this).parent().siblings('div')
.eq(0).html('<span>140</span>').end()
.eq(1).html('<span>Reply</span>');
});
Try
$('textarea').click(function(){
$(this).parent().next().html('<span>140</span>')
.next().html('<span>Reply</span>');
});
http://jsfiddle.net/rVfJ4/
I agree with sweetamylase's answer.
That code should work. Because it is using native javascript, I think it is good for performance.
Or use class name for determinate div:
$('textarea').click(function(){
var parentDiv = $(this).parents('div.post_area2'); // avoid to use $(this) multi time in this function
parentDiv.find('div.word_c').html('<span>140</span>');
parentDiv.find('div.submit').html('<span>Reply</span>');
});
That will work exactly even you add new another div.
Don't use 'eq()' if you have another solution.
I'm sorry, I'm not enough reputation for voting.
I would re-write it as such:
$('textarea').click(function(){
var divContainer = $(this).parent().siblings('div');
divContainer[0].innerHTML = '<span>140</span>';
divContainer[1].innerHTML = '<span>Reply</span>';
});
You can use native attribute innerHTML to set the contents of the DOM element, it is equivalent to using .html() minus the requirement of invoking the jQuery object.
Also, it depends if your coding style likes to do chaining because that's really easily done with jQuery, but can be difficult to read when the line becomes long.
You should give the other divs extra classes to represent what they mean.
$('textarea').click(function(){
$(this).parents('form').children('.hide-until-using-form').show();
});
This creates a highly reusable function that doesn't depend on how many siblings are involved, what the text of the button is (Post? Edit?), etc.
There is infinite ways to do it,
You can think a bit out of the box, i'm not sure that the latest version of jquery optimize the access to elements, if i'm wrong feel free to make me sorry on it.
But here are a few ways to do it:
var parentSelector = $(this).parent().parent();
var parentSelector = $(this).parents('#uniqueId');
var currentId = this.id;
var childrenSelector = parentSelector.children('div[id!="' + currentId + '"'];
var one = $(childrenSelector.get(0));
var two = $(childrenSelector.get(1));
A simple way to optimize the access to elements is to wrap/create new function that save reference to DOM elements, there is some issues that need to be consider but you can make a nice dom-cache library.
Perhaps like this
$('textarea').click(function () {
var parentSiblings = $(this).parent().siblings('div');
$(parentSiblings[0]).html('<span>140</span>');
$(parentSiblings[1]).html('<span>Reply</span>');
});
Or
$('textarea').click(function () {
var parentSiblings = $($(this).parent().siblings('div'));
parentSiblings.eq(0).html('<span>140</span>');
parentSiblings.eq(1).html('<span>Reply</span>');
});
Others may see using vanilla javascript more elegant.
function empty(element) {
"use strict";
while (element.hasChildNodes()) {
element.removeChild(element.lastChild);
}
}
function addEvent(nodeList, type, callBack) {
"use strict";
var length = nodeList.length,
i = 0;
while (i < length) {
nodeList[i].addEventListener(type, callBack, false);
i += 1;
}
}
addEvent(document.getElementsByTagName("textarea"), "click", function (evt) {
"use strict";
var divContainer = evt.target.parentNode.parentNode.getElementsByTagName("div"),
span;
empty(divContainer[1]);
span = document.createElement("span");
span.textContent = "140";
divContainer[1].appendChild(span);
empty(divContainer[2]);
span = document.createElement("span");
span.textContent = "Reply";
divContainer[2].appendChild(span);
});
These and others are available on jsfiddle
I guess it depends on how exactly you define more elegant. Sometimes readability is more important, perhaps not including ant 3rd party libraries and using vanilla javascript.

How to get the first inner element?

So I want to get the first <a> tag in this <div>. This is really driving me nuts. Thanks for any help.
HTML
<div id="PGD" class="album" onmouseover="load(this)">
<a class="dl" href="#">DOWNLOAD</a>
</div>
Javascript
function load(dl)
{
var ID = $(dl).attr('id');
var elemnt = $('ID:first').attr('id');
}
Non-jQuery: (was not tagged with jQuery before, so I included this)
If you want to get the first child element only:
var element = document.getElementById('PGD').children[0];
If you want to get the first anchor element:
var element = document.getElementById('PGD').getElementsByTagName('a')[0];
With jQuery:
var element = $('#PGD').find('a:first');
// or, to avoid jQuery's pseudo selecors:
// var element = $('#PGD').find('a').first();
and actually your function can just be
function load(dl)
{
var element = $(dl).find('a:first');
}
Update:
As you are using jQuery, I suggest to not attach the click handler in your HTML markup. Do it the jQuery way:
$(function() {
$("#PGD").mouseover(function() {
$(this).find('a:first').attr('display','inline');
alert($(this).find('a:first').attr('display'));
});
});
and your HTML:
<div id="PGD" class="album">
<a class="dl" href="#">DOWNLOAD</a>
</div>
​See for yourself: http://jsfiddle.net/GWgjB/
$("#PGD").children("a:first")
This will give you the first child "a" tag, but not the descendents. E.g.
<div id="log">
<p>Foo</p>
Hello
Hello
</div>
Will give you : Hello
$(ID).find(':first')
See find jQuery command.
$('#PGD').find('a:first')
Actualy I've not understanding problem, so I'm trying correct your function, to make it clear for you:
function load(dl)
{
// var ID = $(dl).attr('id');
// var elemnt = $('ID:first').attr('id'); // Here is error-mast be like $(ID+':first')
var ID = $(dl).attr('id');
var elemnt = $(ID).find('*:first').attr('id');
}
I supose dl that is $('#PGD'). But child element A have not attribute id, what are you trying to find?
Also See: http://api.jquery.com/category/selectors/

Categories

Resources