Why is this "this + sibling" call failing? - javascript

I am having some trouble with what I thought should be a simple sibling selector in jQuery.
The problem generates no error message, of course, it simply fails to select properly. Inside a document(ready) function() I have the following simple code to first hide all of the popups, then wait for a person to click an image which will show the sibling pop-up:
//hide all the charm pop ups
$(".charm_pop").hide();
$(".charm > img").click(function() {
$("this + .charm_pop").show();
})
My HTML is being generated by a Django for loop, so there will be many iterations of this simple image/popup combo markup:
{% for ch in charms %}
<div class="charm">
<img src="{{ MEDIA_URL }}images/charms/{{ ch.image }}" alt="{{ ch.name }}" />
<div class="charm_pop">
<p id="charm_name">{{ ch.name }}</p>
<p id="charm_desc">{{ ch.description }}</p>
<p id="charm_price">${{ ch.price }}</p>
<form method="post" action="." class="cart">{% csrf_token %}
<p>**some inputs and what not</p>
</form>
</div>
</div>
{% endfor %}
As you can see, I simply wait for an image to be clicked, and when it is I select it's sibling and reveal that corresponding pop-up. Yet when I click an image, nothing happens. If I replace $("this + .charm_pop").show(); with $(".charm_pop").show(); it does indeed show all of the pop-ups, so the click function is working, the selector is just wonky.
Am I misunderstanding how this is working in this context?

When writing jQuery selectors the string "this" simply means "an HTML element <this>", so $("this + .charm_pop") will certainly not work.
Concatenating the string representation of an object with something else is also not meaningful here, so $(this + " .charm_pop") will also not work.
You should be using appropriate traversal functions instead, starting from $(this):
$(this).next(".charm_pop").show();
There is a number of different ways to go from the clicked image to its sibling .charm_pop, but .next() is fastest and also semantically identical to the adjacent-sibling selector + that you are trying to utilize.

Your code is basically using the + css selector
$("this + .charm_pop").show();
element+.class which says "Selects all the .class elements that are placed immediately after element
In your case it is looking for an element named this. I doubt you have any elements with the tag name of <this>.
Your code needs to be
$(this).siblings(".charm_pop").show();
or
$(this).next(".charm_pop").show();

You can do this :
$(this).find(".charm_pop").show();

Related

Change DOM element class based on text content of another element when generated by Jinja for loop

I need to alter the colour of an HTML element based on the text content of another element. However, the content is being generated with a Python for loop using a Jinja shortcut.
For example:
{% for article in articles %}
<div class="row article">
<div class="col s6">
<strong>{{ article.title }}</strong>
<p>Page count: {{ article.page_count }}</p>
<p>Layout code: <span id="layout">{{ article.layout }}</span></p>
</div>
<div id="show_layout" class="col s1 layout"></div>
</div>
{% endfor %}
I'm trying to use Javascript or jQuery to make the #show_layout element red if a layout code is present (ie. there is some text content in that span). The trouble is that I'm only able to make all or none show up red as the JS function runs once and affects every iteration of the for loop. For example, if 3 article listings are generated by the for loop (pulled from MongoDB) then each of the #show_layout elements turn red, if just one has any layout code content. I've tried experimenting with using the 'this' keyword, but I'm not getting anywhere.
Currently this is the basic function I'm altering, though there have been many different versions! I'm calling this on page load; calling it from the element itself doesn't seem to do anything!
function showLayout() {
let code = document.getElementById("layout").textContent;
let toChange = document.getElementById("show_layout");
if (code !== "") {
toChange.classList.add("layout-red");
console.log(code)
}
else {
console.log("arghghg")
}
}
I'm very new to Python and Jinja, so perhaps my approach is entirely wrong. Can anyone suggest a better way of doing this, or am I missing something obvious?
CBroe pointed me in the right direction here, by suggesting that I do this via Jinja, which had not occurred to me (I'm very, very new to Jinja!).
adding:
{% if article.layout != "" %}
<div id="show_layout" class="col s1 layout-red show_layout"></div>
{% endif %}
did the trick!
Thank you CBroe!

Automatically resizing font-size in ng-repeat

I am currently trying to resize font-size to fit inside a fixed size div. I found this interesting piece of javascript code doing the job very well
$('#defect-text div').css('font-size', '1em');
while ($('#defect-text div').height() > $('#defect-text').height()) {
$('#defect-text div').css('font-size', (parseInt($('#defect-text div').css('font-size')) - 1) + "px");
}
For the following HTML code
<div ng-repeat="defect in surface.defects" class="new-page">
<div id="defect-text">
<div>
<div id="defect-title">{{ 'TYPE' | translate }}:</div>
{{ defect.type }}
<div id="defect-title">Emplacement du défaut:</div>
{{ defect.location }}
<div id="defect-title">Dimension du défaut:</div>
{{ defect.dimension }}
<div id="defect-title">Actions suggérées:</div>
{{ defect.future_operation }}
<div id="defect-title">Détail technique du défaut:</div>
{{ defect.description }}</div>
</div></div>
However, since the different div are inside an ng-repeat expression, my piece of javascript code will resize the font of every container with the desired size of the first one. Is there a way to change to code to select the current element of the ng-repeat and change only it's own css to fit the container?
thanks in advance
I think your problem is actually one of scope:
$('#defect-text div').css('font-size'...)
You're selecting ALL <div> descendants (no matter how deep) of the id="defect-text" element and modifying them at this time. Technically an ID MUST be unique (behavior is not well defined if you have multiple elements with the same ID), and I think your selector is grabbing ALL of the <div> contained within ANY #defect-text elements, which is probably a lot broader than you want, inside of this particular loop.
I would recommend modifying your ng-repeat block and replacing the ID using the angular expression language like
<div id="{{ defect.id }}">
then you can use $("#"+defect.id + " div") to get all <div> descendants of a unique defect.
I'd also consider using the child selector "> div" instead of all descendants.

Write click event dynamically on jquery

Here is the original html
<div class='cl_{{ $gen }}'>{{$gen->name}}</div>
<div class='jl_{{ $var }}'>{{$gen->name}}</div>
After looping over here is the html i got as output
I wanted to do if i click on the class cl_x, then the jl_x should be visible and other should be hidden and by default the first cl_1 should be visible. How can i do this ?
<div class='cl_1'>One</div>
<div class='cl_5'>Two</div>
<div class='cl_6'>Three</div>
<br>
<div class='jl_1'>Alpha</div>
<div class='jl_1'>Andrew</div>
<div class='jl_1'>Christ</div>
<div class='jl_5'>Anto</div>
<div class='jl_5'>Brito</div>
<div class='jl_6'>Oyster</div>
<div class='jl_6'>Beta</div>
Note : All the 1,5,6 are not standard as they are coming from database.
I really can't able to think how to achieve this. Help pls
But Here is what i have tried the logic
Inside Document Ready
Loop over the jquery like html
Write click event to show hide if they cick on cl_*
Trigger the click event for first occurance.
Don't worry about the html generated but the need is to write the jquery events dynamically or something else
But can't able to implement the code pls help
Script :
$(document).ready(function() {
//not sure whether i should loop over the jquery itself or write anything like the element starts with cl-* like that
});
Update :
Here is the Fiddle i have so far
You don't really want to use class for this - a custom data attribute makes sense, though. Like <div class="cl" data-number="{{ $gen }}"> with <div class="jl" data-number="{{$var}}"> on the other elements.
Then inside the $(document).ready(...) you can do something like:
$('.jl').hide().first().show();
$('.cl').click(function(){
$('.jl').hide().filter('[data-number="' + $(this).data('number') + '"]').show();
})
It would also be good to make up more meaningful names than "cl" and "jl" - classes should generally be semantic.
Would move the unique identifiers to a different attribute and add a common class to all the J group
<div class='cl' data-gen="{{ $gen }}">{{$gen->name}}</div>
<div class='jl jl_{{ $var }}'>{{$gen->name}}</div>
JS
$(document).on('click','.cl',function(){
$('.jl').hide().filter('.jl_' + $(this).data('gen') ).show();
})
Add a base "jl" class to all your html that has anything with a "jl_*" so that you will have access to anything overall that has the "jl" class then toggle it hidden or not hidden like so:
HTML:
<div class='cl {{ $gen }}'>{{$gen->name}}</div>
<div class='jl {{ $var }}'>{{$gen->name}}</div>
JS:
$(document).on('click', '.cl', function(e){
var classes = ($(e.target).attr("class").split(' '));
$('.jl').toggleClass("hide");
$('.jl' + "."+ classes[1]).toggleClass("hide");
});
Style:
.hide{
display: none;
}
https://jsfiddle.net/rc368gjp/

jquery toggle in a for loop using Jinga2 template

I am trying to hide/show a div that is within a for loop using jquery's toggle. When I click the button to toggle, the div slides out for a quick moment, but then hides again. When this happens, the toggle button almost seems disabled for the next click...then works again with the same problematic div display. I used the {{email.sender}} template value because when I clicked on the toggle button, all the items in the list would be activated instead of just that one. The below code is inserted into a tab with Jquery (this part is working). Thanks for any advice you can give on this-
<div id="email_received_list">
{% for email in email_received_list %}
<p>
<input type="button" id="{{email.sender}}" value="Show Message"> {{email.sender}}: {{ email.subject }}
</p>
<script>
$(document).ready(function(){
$('#{{email.sender}}').click(function() {
$('.{{email.sender}}').slideToggle('fast');
return false;
});
});
</script>
<div class="{{email.sender}}" style="display:none; background-color:#4CF;width:100%;height:100%;"></div>
{% else %}
(You have not received any messages yet.)
{% endfor %}
Ok, I feel stupid on this one - but once I added some actual content to the div (not just trying to color a square) it worked fine. I guess since it was in a loop, it was trying to size the div match the content. When there was no content, the div just hid itself again. If you know something different on this, please let me know -thanks.

Show div on mouseover image, hide div on mouseout: nextSibling and previousSibling not working

I have the following code:
<div class="outer-div">
<img onmouseover="showDiv(this)" src="{{ STATIC_URL }}/images/user_male.png">
<a onmouseout="hideDiv(this)" href="{% me.some_url %}" style="display: block;">
<div class="inner-block" onmouseout="hideDiv(elem)">
<h5>{{ me.title }}</h5>
<p>{{ me.text }}</p>
<p>about ? ago</p>
</div>
</a>
<div>
<p>Comments</p>
<p>Likes</p>
</div>
</div>
<script>
function showDiv(elem) {
elem.style.visibility="hidden";
elem.nextSibling.style.visibility="visibile";
}
function hideDiv(elem) {
elem.style.visibility="hidden";
elem.previousSibling.style.visibility="visibile";
}
</script>
the div "inner-block" is positioned so it goes directly above the image when u hover. So, the idea is to have an onmouseover for the image that pops up the linked "inner-block" div, and then onmouseout on the link that hides the div and shows the image again.
When I try to use elem.nextSibling, I get the error that elem.nextSibling is undefined, and thus you can't set the visibility. Thanks! Alternately, is there a different way to do this? thank you!
I think you are getting it wrong nextSibling and previousSibling is work on the same parent
but your <div class="inner-block" onmouseout="hideDiv(elem)"> has no sibling
more detail :
http://www.w3schools.com/dom/prop_node_nextsibling.asp
I think you'd better set element id and use getElementById instead of sibling
Ok, so now I'm getting the error Uncaught ReferenceError: elem is not defined
Remember, always fix the first error that appears.
Ok the closest I can get is this jsFiddle.
Anyone feel free to build upon my answer.
Alright well here is my attempt:
<div id="outer-div">
<img id ="img" onmouseover="showDiv()" src="http://www.crunchbase.com/assets/images/resized/0004/1621/41621v6-max-250x250.jpg" style = "visibility:visible;"/>
<div id="inner-block" onmouseout="hideDiv()" style="visibility:hidden;">
<a href="http://www.stackoverflow.com" style="display: block;">
<div>
<h5>{{ me.title }}</h5>
<p>{{ me.text }}</p>
<p>about ? ago</p>
</div>
</a>
</div>
<div>
<p>Comments</p>
<p>Likes</p>
</div>
</div>
And here is the javascript:
<script>
function showDiv() {
document.getElementById("img").style.visibility = "hidden";
document.getElementById("inner-block").style.visibility = "visible";
}
function hideDiv() {
document.getElementById("inner-block").style.visibility = "hidden";
document.getElementById("img").style.visibility = "visible";
}
</script>
Couple of things: main changes I did was instead of putting the function on the anchor tag, I encapsulated that all into its own div. I think this is a lot better if you specify a specific area which in this case is the div around it. This makes it better for the future if say you didn't want it to be links but a certain section, etc.
I also used document.getElementById. I'm pretty sure its possible to do it with previousSibling and nextSibling (as to why there was a undefined error still kinda confuses me so I'd have to play around with it more) but overall I think this is better style anyways. Unless you ALWAYS wanted to get the sibling before and the sibling after the element, I would prefer it you do it this way -- specifying which elements you want to disappear and reappear is a lot more organized.
Right now the links aren't directly above the image. If you want to do that, I think you could play around with absolute positioning or floats to get it to work but I'll let you try that for yourself. Hope this helps.
Well, I figured out the problem. elem.nextSibling and elem.previousSibling were both returning text nodes.
So I ended up using
elem.nextSibling.nextSibling
and
elem.previousSibling.previousSibling
Woohoo! always debug-

Categories

Resources