So I have this HTML:
<div class="tip-box">
<div class="tip-title" onclick="toggleTip()">
<h2>Tip 1</h2>
</div>
<div class="tip-content hidden">
<p>Tip 1 content</p>
</div>
</div>
And this Javascript:
function toggleTip() {
$(this).siblings(".tip-content").toggleClass("hidden");
}
Hopefully it's obvious what this is supposed to do, but it doesn't work. Using .siblings() just doesn't seem to work in this way.
What's the correct solution for this? To get the next sibling of a certain type or with a certain class and then hide/show it?
You can use Jquery function.
<div class="tip-box">
<div class="tip-title">
<h2>Tip 1</h2>
</div>
<div class="tip-content hidden">
<p>Tip 1 content</p>
</div>
</div>
$(document).ready(function(){
$('.tip-title').click(function(){
$(this).siblings(".tip-content").toggleClass("hidden");
});
});
you can also use this
<div class="tip-box">
<div class="tip-title" onclick="toggloTip(this)">
<h2>Tip 1</h2>
</div>
<div class="tip-content hidden">
<p>Tip 1 content</p>
</div>
</div>
<script>
function toggloTip(elm) {
$(elm).siblings(".tip-content").toggleClass("hidden");
}
</script>
You can use pure javaScript with nextElementSibling property of node something like below,
I suppose you want do this operation with siblings.
function getChildrens(n, selector) {
var nodes = [];
while (n.nextElementSibling != null) {
if (n.nextElementSibling.hasOwnProperty('classList')) {
if (n.nextElementSibling.classList.contains(selector)) {
//return n.nextElementSibling;
nodes.push(n.nextElementSibling);
}
}
n = n.nextElementSibling;
}
return nodes;
};
function getSiblings(n, selector) {
return getChildrens(n, selector);
}
function toggleTip(elem) {
var siblings = getSiblings(elem, "tip-content");
if (siblings.length > 0) {
for (var i = 0; i < siblings.length; i++) {
siblings[i].classList.toggle("hidden");
}
}
}
.hidden {
display: none;
}
<div class="tip-box">
<div class="tip-title" onclick="toggleTip(this)">
<h2>Tip 1</h2>
</div>
<div class="tip-content hidden">
<p>Tip 1 content</p>
</div>
</div>
Here is another non JQuery answer.
To get the next element sibling use:
var nextElement = element.nextElementSibling;
To get the previous element sibling use:
var previousElement = element.previousElementSibling;
To get the element index use:
var index = Array.prototype.slice.call(element.parentElement.children).indexOf(element);
If you are at the first element the previousElementSibling value will be null.
If you are at the last element the nextElementSibling value will be null.
How about this JavaScript:
$(function(){
$('.tip-box').on('click', '.tip-title', function(){
$(this).next('.tip-content').toggleClass('hidden');
});
});
Remove the idea of working with onclick attributes when you use jQuery.
None of the previous answers, not even that serial-upvoted one ;), actually explains the problem and why their solutions work.
The problem is that an inline onclick handler does not pass on its current context. Inside the onclick="" JavaScript code this is the element clicked. Once you call a global function (like your toggleTip), that context is lost. The this the function receives is window and not the element.
The usual quick fix, for raw JavaScript code, is to pass this as a parameter to the global function.
e.g.
onclick="toggleTip(this)"
and receive a parameter in the function like this:
function toggleTip(element) {
$(element).siblings(".tip-content").toggleClass("hidden");
}
However, as you are using jQuery, inline event handlers are actually a bad idea. They separate the event registration from the event handler code for no reason and do not allow for multiple event handlers, of the same type, on the same element. They also bypass the rather cool event bubbling system jQuery uses.
The preferred alternative, with jQuery, is to use jQuery to select the element and jQuery to connect the event in one step:
jQuery(function($){
$('.tip-title').click(function(){
$(this).siblings(".tip-content").toggleClass("hidden");
});
});
As you only want the element that follows, and potentially will add more pairs, the better option would be using nextAll and first(), with the same jQuery filter, instead of siblings:
e.g.
jQuery(function($){
$('.tip-title').click(function(){
$(this).nextAll(".tip-content").first().toggleClass("hidden");
});
});
Or, of you can guarantee it is the next element, use next as #Tim Vermaelen did (with or without the selector makes no difference, so might as well leave it out):
jQuery(function($){
$('.tip-title').click(function(){
$(this).next().toggleClass("hidden");
});
});
Note: In this example jQuery(function($){ is a DOM ready event handler which is the rather handy shortcut version of $(document).ready(function(){YOUR CODE});, which also passes a locally scoped $ value. For those that mistake this code for an incorrect IIFE, here is a working example: http://jsfiddle.net/TrueBlueAussie/az4r27uz/
Related
In HTML, I have
<div>
<button onclick="action()">button</button>
</div>
Without giving an ID or class to the div element, what can I do in JavaScript to get an access to it and use it?
Pass this into action:
<button onclick="action(this)">button</button>
and then in action
function action(btn) {
var div = btn.parentNode;
// ...
}
or if you want a bit more flexibility, use the (relatively-new) closest method:
function action(btn) {
var div = btn.closest("div");
// ...
}
Side note: Rather than onxyz-attribute-style event handlers, consider using modern event handling (addEventListener, attachEvent on obsolete browsers). If you have to support obsolete browsers, my answer here provides a function you can use to deal with the lack of addEventListener.
Grab the event then get the currentTarget then find the parentNode. Very simple way to do it. This will work no matter which element is clicked. Please see code snippet demonstration.
function getParentNode(event) {
console.log(event.currentTarget.parentNode);
}
<div id="div1"><button onclick="getParentNode(event)">Click</button></div>
<div id="div2"><button onclick="getParentNode(event)">Click</button></div>
using this in the onclick=action() and then the parent is .parentNode
then your method would look like :
<div>
<button onclick="action(this);">button</button>
</div>
function action(el) {
console.log(el.parentNode);
}
but I rather prefer .addEventListenerand for your next question then :
var $el = document.getElementById("a");
$el.addEventListener("click", function(e) {
// Create a <button> element
var btn = document.createElement("BUTTON");
// Create a text node
var t = document.createTextNode("CLICK ME");
// Append the text to <button>
btn.appendChild(t);
// Append <button> to the parentNode
this.parentNode.appendChild(btn);
});
<div>
<button id="a">Button</button>
</div>
Also in case you wonder for a shorter version and probably introducing to the jQuery :
;$(function(){
$("#a").on("click", function(e) {
$(this).parent().append('<button>Click Me</button>');
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<button id="a">Button</button>
</div>
Another but not straight way to get the parent instead of .parentNode is .closest() method:
element.closest("div"); // also supported in jQuery
Returns the first ancestor of element, that is a <div> element:
If it is possible, pass this (button) to action. Then you can access parent by element.parentNode. Example: https://jsbin.com/fidufa/edit
This is probably a very easy approach, however I haven't been able to figure it out.
My approach is to get all <img> elements that have the "expanded-image" class that are within the "img-preview" of my current "entry".
This is my html:
<div class="entry">
<div class="img-preview">
<img>
<img class="expanded-image" style="display:none;">
</div>
<div class="content">
[..]
[..]
<span class="more-text"></span>
[..]
[..]
</div>
</div>
And this is the JS I work with:
$('.content').each(function(event) {
$(this).find('span.more-text').click(function(event) {
// TODO
});
});
Firstly you don't need the each() at all as you can apply the click() event handler to all elements within a single selector.
To solve your issue you can use closest() to find the nearest parent .entry element to the clicked .more-text. From there you can find() all the .expanded-image elements. Try this:
$('.content span.more-text').click(function(event) {
var $imgs = $(this).closest('.entry').find('.img-preview .expanded-image');
// work with $imgs here...
});
$('.content').each(function(event) {
var $content = $(this);
$(this).find('span.more-text').click(function(event) {
$content.parent().find('.expanded-image'); // there you go
});
});
Use o combination of closest, prev and find
$('span.more-text').click(function(event) {
$(this).closest('.content').prev().find('.expanded-image');
});
You can use closest() to bubble up to find the parent given in the selector. Then from there you can navigate down to the required elements.
$(this).closest('.entry').find('.img-preview .expanded-image');
Also as Ron has suggested you dont have to loop and then bind the click events. You can simply do
$('.entry .content .more-text').click(function(){
$(this).closest('.entry').find('.img-preview .expanded-image');
})
I have the following code:
.recipe
.ingredients
= f.simple_fields_for :ingredients do |ingredient|
= render 'ingredient_fields', f: ingredient
.row#links
.col-xs-12
= link_to_add_association "", f, :ingredients
%hr
I need to select the ingredients div using jquery in the format of $("#links")["closest"](".recipe > .ingredients") but this doesn't select anything.
It's frustrating though as $("#links")["closest"](".recipe > .row") will return the correct div.
Fiddle of what works and what I want: https://jsfiddle.net/yL6dr4s1/
According to jQuery documentation, closest method tries to find element matching the selector by testing the element itself and
traversing up through DOM.
It does not go through siblings of the element.
Based on your requirements, it seems like you want to traverse the tree for getting match in siblings. jQuery has siblings method to do that. So one solution would be to use siblings method like:
$("#links")["siblings"](".recipe > .ingredients")
Another soultion would be to get closest parent and then use children as answered by #mhodges
As for the query $("#links")["closest"](".recipe > .row"):
It works fine because closest method finds the match in the element itself.
Here is the example to showcase that:
$(document).ready(function() {
// Match found because it is parent
console.log($("#links")["closest"](".wrapper").length);
// No match found because element is sibling
console.log($("#links")["closest"](".row1").length);
// No match found because element is sibling
console.log($("#links")["closest"](".row3").length);
// Match found because it is element itself
console.log($("#links")["closest"](".row2").length);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div class="row1">
<span>Content1</span>
</div>
<div class="row2" id="links">
<span>Content2</span>
</div>
<div class="row3">
<span>Content3</span>
</div>
</div>
I am not sure of your requirements on using the exact selector/syntax you provided, but this selector works exactly how you want it to.
$(this).closest(".recipe").children(".ingredients").append('<br/><input type="text" value="Flour">');
Edit
This is the closest I could get:
$(this)["closest"](".recipe").children(".ingredients").append('<br/><input type="text" value="Flour">');
I don't think you can use the selectors in the way you propose.
As far as the DOM is concerned (and jQuery), the element defined by ingredient and the element defined by row are not related. You have to traverse up to the parent element, then back down to get to the child.
Here is a fiddle that hopefully demonstrates the issue.
If you can change it so that ingredient and row are both within the same parent div, you might have more luck with your test selector syntax.
When jQuery gets to buggy, doesn't have a certain option or just becomes to messy to use for a certain operation, it is good we also have access to good old plain javascript.
document.querySelector('#addToIngredients').addEventListener('click' , function(e) {
var recipe = getClosest(e.target,'recipe');
if (recipe) {
var ingred = recipe.querySelector('.ingredients');
ingred.innerHTML += '<br/><input type="text" value="Flour">';
}
});
function getClosest(elem,cls) {
var el = elem.parentNode;
while (el){
if (el.className.indexOf(cls) > -1) {
return el;
}
el = el.parentNode;
}
return false;
}
<div class="recipe">
<div class="ingredients">
<input type="text" value="Eggs"><br/>
<input type="text" value="Flour">
</div>
<div class="row">
<div class="col-xs-12">
Add to .ingredients
</div>
</div>
<hr/>
</div>
Of course they can be combined
$(function() {
$("#addToIngredients").on('click', function(e) {
var recipe = getClosest(e.target,'recipe');
if (recipe) {
var ingred = recipe.querySelector('.ingredients');
ingred.innerHTML += '<br/><input type="text" value="Flour">';
}
});
})
So I have a mini slide menu in my website there is a menu you can choose what you want to read. There are points to click, when u clicked it the point get a red background.
But there is a problem.
When i click one point and then an other point the first clicked point have to lose his background.
Here is my HTML:
<div id="slide_button" onClick="clicked(this);"><dir class="button_1"></dir></div>
<div id="slide_button" onClick="clicked(this);"><dir class="button_2"></dir></div>
<div id="slide_button" onClick="clicked(this);"><dir class="button_3"></dir></div>
<div id="slide_button" onClick="clicked(this);"><dir class="button_4"></dir></div>
<div id="slide_button" onClick="clicked(this);"><dir class="button_5"></dir></div>
Here is my JS:
function clicked(slide_button) {
slide_button.getElementsByTagName("dir")[0].style.backgroundColor="red";
}
HERE IS AN EXAMPLE ON FIDDLE.
My "QUESTION IS" what i have to do to solve that?
What should I pay attention?
First you need to fix your HTML becaue your id values aren't unique. In fact, you don't even need id values, so you should use "slide_button" as a class. You can then use it to select all the buttons:
<div onClick="clicked(this);" class="slide_button"><dir></dir></div>
<div onClick="clicked(this);" class="slide_button"><dir></dir></div>
<div onClick="clicked(this);" class="slide_button"><dir></dir></div>
<div onClick="clicked(this);" class="slide_button"><dir></dir></div>
<div onClick="clicked(this);" class="slide_button"><dir></dir></div>
The CSS needs to be changed now so "slide_button" is a class selector, instead of an id selector:
.slide_button {
display: inline-block;
}
As for clearing the background, clear all of them before coloring the selected one red:
function clicked(slide_button) {
var buttons = document.getElementsByClassName('slide_button');
for(var i = 0; i < buttons.length; i++) {
buttons[i].getElementsByTagName('dir')[0].style.backgroundColor = '';
}
slide_button.getElementsByTagName('dir')[0].style.backgroundColor = 'red';
}
jsfiddle
This uses just JavaScript with no JQuery, but if you are using JQuery, you might as well use it here. The code is a lot shorter and easier to follow.
Here's a JQuery version:
$(function() {
$('.slide_button').click(function() {
var $button = $(this);
$button.children(':first').css({ backgroundColor: 'red' });
$button.siblings().children(':first').css({ backgroundColor: '' });
});
});
Note: This registers a click-handler, so you can get rid of the "onclick" attirbutes.
jsfiddle
You have to select all other points and set their background to none.
Or remeber which point is selected and on select another just remove background on last and remeber current point, then set its background to red.
See fiddle: http://fiddle.jshell.net/399Dm/5/
At first id should be unique per element.
<div class="slide_button"><dir class="button"></dir></div>
<div class="slide_button"><dir class="button"></dir></div>
<div class="slide_button"><dir class="button"></dir></div>
<div class="slide_button"><dir class="button"></dir></div>
<div class="slide_button"><dir class="button"></dir></div>
Second, you should store reference of clicked element if you want later remove background color, and instead of inline event handlers or binding all elements would be better if you use event delegation.
Demonstration
(function () {
"use strict";
// getting parent node of divs, due to bind click event. then
var ele = document.querySelector(".slide_button").parentNode,
prev = null; // store previous clicked element
ele.addEventListener("click", clickHandler); // event handler.
function clickHandler(e) {
var t = e.target; // get target of clicked element
// filter by target node name and class. edit: removed class checking
if (t.nodeName.toLowerCase() === "dir") {
// checking value of prev !== null and it's not same element.
if (prev && prev !== t) {
prev.style.backgroundColor = "";
}
prev = t; // store clicked element
t.style.backgroundColor = "red";
}
}
}());
I have fixed the fiddle so that it works hopefully as you plan.
http://jsfiddle.net/399Dm/8/ There you go!
var forEach = function(ctn, callback){
return Array.prototype.forEach.call(ctn, callback);
}
function clear(element, index, array) {
element.getElementsByTagName("dir")[0].style.backgroundColor="";
}
function clicked(slide_button) {
forEach(document.getElementsByClassName("slide_button"), clear);
//.style.backgroundColor="";
slide_button.getElementsByTagName("dir")[0].style.backgroundColor="red";
}
I had a slightly different method than #atlavis but a similar result.
http://fiddle.jshell.net/2AGJQ/
JSFIDDLE DEMO
jQuery
$('.slide_button').click(function(){
$('.slide_button dir').css("background-color", "inherit");
$(this).find('dir').css("background-color", "red");
});
HTML - Your markup is invalid because you have duplicate ids. Make them classes as below instead.
<div class="slide_button" >
<dir class="button_1"></dir>
</div>
<div class="slide_button">
<dir class="button_2"></dir>
</div>
<div class="slide_button">
<dir class="button_3"></dir>
</div>
<div class="slide_button">
<dir class="button_4"></dir>
</div>
<div class="slide_button">
<dir class="button_5"></dir>
</div>
CSS change
.slide_button {
display: inline-block;
}
If you can look at the following jsfiddle, I used jQuery to get what you want.
I just created script that shows/hides (toggles) block of HTML. There are four buttons that each can toggle its HTML block. When any HTML block is opened, but user has been clicked on other button than that HTML block's associated button... it hides that HTML block and shows new one.
Here is what I have at the moment:
$('.btn_add_event').click( function() {
$('.block_link, .block_photos, .block_videos').hide();
$('.block_event').toggle();
});
$('.btn_add_link').click( function() {
$('.block_event, .block_photos, .block_videos').hide();
$('.block_link').toggle();
});
$('.btn_add_photos').click( function() {
$('.block_event, .block_link, .block_videos').hide();
$('.block_photos').toggle();
});
$('.btn_add_videos').click( function() {
$('.block_event, .block_link, .block_photos').hide();
$('.block_videos').toggle();
});
Any ideas how to reduce code size? Also, this script isn't very flexible. Imagine to add two new buttons and blocks.
like Sam said, I would use a class that all the blocks share, so you never have to alter that code. Secondly, you can try 'traversing' to the closest block, therefore avoiding it's name. That approach is better than hard coding each specific block, but if the html dom tree changes you will need to refactor. Last, but best, you can pass in the class name desired block as a variable to the function. Below is something you can copy paste that is close to what you started with.
$('.myAddButtonClass').click( function() {
$('.mySharedBlockClass').filter(':visible').hide();
//find a good way to 'traverse' to your desired block, or name it specifically for now.
//$(this).closest(".mySharedBlockClass").show() complete guess
$('.specificBlockClass').show();
});
I kept reading this "When any HTML block is opened, but user has been clicked on other button than that HTML block's associated button" thinking that my eyes were failing me when Its just bad English.
If you want to make it more dynamic, what you can do is add a common class keyword. Then
when the click event is raise. You can have it loop though all the classes that have the
keyword and have it hide them all (except the current one that was clicked) and then show the current one by using the 'this' keyword.
you can refer below link,
http://chandreshmaheshwari.wordpress.com/2011/05/24/show-hide-div-content-using-jquery/
call function showSlidingDiv() onclick event and pass your button class dynamically.
This may be useful.
Thanks.
try this
$('input[type=button]').click( function() {
$('div[class^=block]').hide(); // I resumed html block is div
$(this).toggle();
});
Unfortunatly I couldn't test it, but if I can remember right following should work:
function toogleFunc(clickObject, toogleTarget, hideTarget)
{
$(clickObject).click(function()
{
$(hideTarget).hide();
$(toogleTarget).toggle();
});
}
And the call:
toogleFunc(
".btn_add_videos",
".block_videos",
".block_event, .block_link, .block_photos"
);
and so far
Assuming the buttons will only have one class each, something like this ought to work.
var classNames = [ 'btn_add_event', 'block_link', 'block_photos', 'block_videos' ];
var all = '.' + classNames.join(', .'); // generate a jquery format string for selection
$(all).click( function() {
var j = classNames.length;
while(j--){
if( this.className === classNames[j] ){
var others = classNames.splice(j, 1); // should leave all classes but the one on this button
$('.' + others.join(', .')).hide();
$('.' + classNames[j]).toggle();
}
}
}
All the buttons have the same handler. When the handler fires, it checks the sender for one of the classes in the list. If a class is found, it generates a jquery selection string from the remaining classes and hides them, and toggles the one found. You may have to do some checking to make sure the strings are generating correctly.
It depends by how your HTML is structured.
Supposing you've something like this
<div class="area">
<div class="one"></div>
<div class="two"></div>
<div class="three"></div>
</div>
...
<div class="sender">
<a class="one"></a>
<a class="two"></a>
<a class="three"></a>
</div>
You have a class shared by the sender and the target.
Your js would be like this:
$('.sender > a').click(function() {
var target = $(this).attr('class');
$('.area > .' + target).show().siblings().hide();
});
You show your real target and hide its siblings, which aren't needed.
If you put the class postfixes in an array, you can easily make this code more dynamic. This code assumed that it doesn't matter in which order toggle or hide are called. If it does matter, you can just remember the right classname inside the (inner) loop, and toggle that class after the loop.
The advantage to this approach is that you can extend the array with an exta class without needing to modifying the rest of the code.
var classes = new Array('videos', 'event', 'link', 'photos');
for (var i = 0; i < classes.length; ++i)
{
$('.btn_add_' + classes[i]).click(
function()
{
for (var j = 0; j < classes.length; ++j)
{
if (this.hasClass('btn_add_' + classes[j]))
{
$('.block_' + classes[j]).toggle();
}
else
{
$('.block_' + classes[j]).hide();
}
}
});
}
You could make this code more elegant by not assigning those elements classes like btn_add_event, but give them two classes: btn_add and event, or even resort to giving them id's. My solution is based on your description of your current html.
Here is what I think is a nice flexible and performant function. It assumes you can contain your links and html blocks in a parent, but otherwise it uses closures to precalculate the elements involved, so a click is super-fast.
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js" ></script>
<script type="text/javascript">
// Enables show/hide functionality on click.
// The elements within 'container' matching the selector 'blocks' are hidden
// When elements within 'container' matching the selector 'clicker' are clicked
// their attribute with the name 'clickerAttr' is appended to the selector
// 'subject' to identify a target, usually one of the 'blocks'. All blocks
// except the target are hidden. The target is shown.
//
// Change clickerAttr from 'linkTarget' to 'id' if you want XHTML compliance
//
// container: grouping of related elements for which to enable this functionality
// clicker: selector to element type that when clicked triggers the show/hide functionality
// clickerAttr: name of the DOM attribute that will be used to adapt the 'subject' selector
// blocks: selector to the html blocks that will be shown or hidden when the clicker is clicked
// subject: root of the selector to be used to identify the one html block to be shown
//
function initToggle(container,clicker,clickerAttr,blocks,subject) {
$(container).each(
function(idx,instance) {
var containerElement = $(instance);
var containedBlocks = containerElement.find(blocks);
containerElement.find(clicker).each(function(idxC, instanceClicker) {
var tgtE = containerElement.find(subject+instanceClicker.getAttribute(clickerAttr));
var clickerBlocks = containedBlocks.not(tgtE);
$(instanceClicker).click(function(event) {
clickerBlocks.hide();
tgtE.toggle();
});
});
// initially cleared
containedBlocks.hide();
}
);
}
$(function() {
initToggle('.toggle','a.link','linkTarget','div.block','div.');
});
</script>
</head>
<body>
Example HTML block toggle:
<div class="toggle">
a <br />
b <br />
c <br />
<div class="A block"> A </div>
<div class="B block"> B </div>
<div class="C block"> C </div>
</div> <!-- toggle -->
This next one is not enabled, to show scoping.
<div class="toggle2">
a <br />
<div class="A block">A</div>
</div> <!-- toggle2 -->
This next one is enabled, to show use in multiple positions on a page, such as in a portlet library.
<div class="toggle">
a <br />
<div class="A block">A</div>
</div> <!-- toggle (2) -->
</body>
</html>