My CODE
HTML:
<p id="console"></p>
<button>Click <span class="icon"></span>
</button>
JS:
$('.icon').click(function () {
$('#console').html('Icon has been clicked');
return false;
});
$('button').click(function () {
$('#console').html('Button has been clicked');
});
CSS:
.icon {
background-position: -96px 0;
display: inline-block;
width: 14px;
height: 14px;
margin-top: 1px;
line-height: 14px;
vertical-align: text-top;
background-image: url("http://twitter.github.com/bootstrap/assets/img/glyphicons-halflings.png");
background-repeat: no-repeat;
}
Demo
Problem
I am able to click on .icon in Chrome , but not in Firefox. When I click on .icon, it clicks on whole button.
Question:
Isnt my code valid ? If my code is valid, whats the solution to this problem.
What I have tried
I have tried doing $('.icon').click() from console and it works perfectly in ff, so I guess the problem is that span is not clickable inside button.
e.preventDefault() and e.stopPropagation are not working either.
I've tried putting inside span but its not working either.
Refer to the spec, most notably the forbidden contents (in the SGML definition; for assistance reading that, look here): as, forms, form "controls" (input, select, etc), and fieldsets.
While you are correct in asserting that spans (and divs, etc) are legal contents of a button element, the illegal elements are all to do with having button content that does anything other than layout / styling.
I don't see anything in the spec precluding what you're trying to do, but I do see a lot discouraging it, and would be unsurprised if various browsers also "discouraged" that by not supporting it.
Which is to say: find another way to do what you want if you want to have cross-browser support. I don't understand what you're actually trying to do, so I don't think its possible to propose alternatives. I get that you want to respond differently to clicking on the button vs the icon -- but that's a (good, btw) demonstration of what you don't want to happen, not an explanation of an actual problem you want to solve.
One way might be to not use a button, and instead use another span or a div:
<p id="console"></p>
<div class="button_replace">Click <span class="icon"></span></div>
<script>
$('.icon').click(function () {
$('#console').html('Icon has been clicked');
return false;
});
$('.button_replace').click(function () {
$('#console').html('Button has been clicked');
});
</script>
If you're here, maybe this solution will work for you, even though it's not really related directly to the question.
If you've applied a
$("button").click() listener, and
your button contains a <span> or any other <tag>, and
your .click callback function refers to $(this) (or even this)
Then, if you click on the button, this will likely be the top-most tag you CLICKED ON.
This will often, such as in my case, misattribute the caller, causing script errors.
Hope it helps someone out there!
Using previous answers, I found that just changing my to fixed the problem and allowed me to have content inside the button.
The styling is virtually the same, I just left the same bootstrap button classes and element in there and it behaves just the same as before. The tabindex is there because I'm using a dropdown list inside the button so that the button (now div) can be focused and have (blur) or (focusout) event in Angular.
Related
I have a situation similar to this one where I have a button that I need to have no pointer events AND show a not-allowed cursor.
The answer given by #Petr Odut worked spectacularly (many thanks) except for the part about the tab index and on focus.
The solution he gave to that problem involves hard coding the tab index and on focus attributes into the specific element. However my element has its disabled class added programmatically with jQuery. Sometimes it is disabled, sometimes it isn't, so I cannot hard code those attributes.
I know I could set those attributes with jQuery at the same time I set the disabled class, but this button is not the only button I'd like to have this functionality for. I'd like some way to set the tab index/on focus globally, so that any disabled button exhibits that behavior.
So, is there a way to do that?
Note: I'm still new on stackoverflow and don't have enough reputation to ask this question directly in a comment on the post, which is why I am doing this. I apologize if asking this way is improper etiquette.
Also, much thanks Petr.
Something like this?
and pure JS version:
[...document.querySelectorAll('a.disabled')].forEach(a => {
a.setAttribute("tabindex", "-1");
a.setAttribute("onfocus", "this.blur()");
console.log(a.getAttribute("tabindex"));
})
$("a.disabled").each(function(i) {
$(this).attr('tabindex', "-1").attr('onfocus', "this.blur()");
console.log($(this).attr('tabindex'))
});
/* Adding cursor just works: */
.disabled {
cursor: not-allowed;
}
/* Makes link non-clickable: */
.disabled:active {
pointer-events: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Disabled link
Disabled link2
NOT Disabled link3
So I have a snippet that I'm using to build some buttons.
<font color=white><button class="button"><span>Register</span></button></font>
<button class="button" onclick="window.location='http://www.google.com';"><span>SP Training</span></button>
<button class="button"><span>Assistance</span></button>
<button class="button"><span>Orders</span></button>
<button class="button"><span>KM Milsuite</span></button>
<button class="button"><span>TMT</span></button>
As you can see I have tried wrapping the whole thing in href, I have tried wrapping the span in href, I have tried wrapping just the font in href, all failed
Ok so I trekked down the java world and tried some on click (numerous variations I have found on this site) none of which work! Every button is a clickable but EVERY button simply links back to the page i'm currently working on. By no means am I an expert at all this but I expected a little give on this!
Any suggestions?
The purpose of a button is to either:
Submit a form (type="submit", the default)
Allow JavaScript to be triggered (type="button")
As you can see I have tried wrapping the whole thing in href
The HTML specification forbids that.
I have tried wrapping the span in href
The span appears to serve no purpose
Every button is a clickable but EVERY button simply links back to the page i'm currently working on
If clicking the button is reloading the current page, then it is probably a submit button inside a form with an action attribute that resolves to the current page (or no action attribute).
If you want a link then use a link and do not use a button.
If you want your link to look like a button, then use CSS to style it that way. Note that the :active pseudo-class is useful for achieving the 3d depressed effect when the link is clicked.
The span tag inside your button is catching the click action. You must take the span out of the "bubbling" chain.
The easiest way is to apply CSS and add the class to your span tags.
span.nonclickable {
pointer-events: none;
}
After that you can catch the button clicks.
A more detailed explanation can be found here: Use CSS to make a span not clickable
It is not quite clear what you want these buttons to do. Use a-tags to link to other pages and use buttons to refer to an action in javascript or a form submit.
You could try this :
<input type="button" onclick="myF()" />
<script>
function myF() {
window.open('http://www.google.com', '_blank', 'resizable=yes');
}
</script>
Hope it helps
So this follows along with some similar questions that have been asked on here but mine is a very specific scenario I'm trying to work within. The situation is I'm on the production end of a CMS and I need to manipulate a forms module in a very specific way but my current access is very limited. In plain english, I need to be able to click a button, have that click append a specific string into a text field and then trigger the click event on another button after this string has been appended. What I have so far, while not perfect, seems like it should work but is not. And yes I'm aware that this is a very odd, roundabout way of doing this but it's a short-term hotfix to accommodate a deadline so trust me when I say this approach is the only current option.
The code:
CSS:
<style>
.form-group {
visibility: hidden;
}
input[type="submit"] {
visibility: hidden;
}
</style>
Js:
<script>
$("#yes").click(function(){
$('#yesHide').contents().appendTo('#textFieldID');
$('#submitID').trigger('click');
});
$("#no").click(function(){
$('#noHide').contents().appendTo('#textFieldID');
$('#submitID').trigger('click');
});
</script>
HTML:
<a id="yes">yes</a>
<a id="no">no</a>
<p id="yesHide" style="visibility: hidden;">This page was helpful</p>
<p id="noHide" style="visibility: hidden;">This page was not helpful</p>
If the button lives in a form, you can try triggering the submit on the form like this:
$('#formID').trigger('submit');
Otherwise, you'll need an event handler on your #submitID button that actually does something, otherwise you'll be triggering an event that isn't being listened for.
The code below works fine with ONE Reveal/Hide Text process
<div class="reveal">Click Here to READ MORE...</div>
<div style="display:none;">
<div class="collapse" style="display:none;">Collapse Text</div>
However if this code is duplicated multiple times, the Collapse Text shows up and doesn't disappear and in fact conflicts with the Expand to reveal even more text instead of collapsing as it should.
In this http://jsfiddle.net/syEM3/4/ click on any of the Click Here to READ MORE...
Notice how the Collapse Text shows up at the bottom of the paragraphs and doesn't disappear. Click on the Collapse and it reveal more text.
How do I prevent this and getting to work as it should?
The two slideDown function calls are not specific to the .reveal and/or .collapse that you are currently doing. i.e.
$(".collapse").slideDown(100);
will find all the elements with the class .collapse on the page, and slide them down. irrespective of what element you just clicked.
I would change the slideDown call to be relavant to the element you just clicked i.e. something like this
$('.reveal').click(function() {
$(this).slideUp(100);
$(this).next().slideToggle();
$(this).next().next(".collapse").slideToggle(100);
});
in your code
$('.reveal').click(function() {
$(this).slideUp(100);
$(this).next().slideToggle();
$(".collapse").slideDown(100);
});
$('.collapse').click(function() {
$(this).slideUp(100);
$(this).prev().slideToggle();
$(".reveal").slideDown(100);
});
this two rows doesn’t do what you want as they act on all elements of the specified class
$(".reveal").slideDown(100);
$(".collapse").slideDown(100);
When you do $(".collapse").slideDown(100);, jQuery runs slideDown on everything with the .collapse class, not just the one that's related to your current this. To fix this, refer to the collapse based on its location to $(this).
Do do this, use something like $(this).siblings(".collapse").slideDown(100);
Note that this particular selector will only work if you enclose each text block in its own div. With each text element in its own div, like you have it now, .siblings(".collapse"), which selects all the siblings of $(this) with the collapse class, will still select both of the collapse elements.
Okay, I think you should take a different approach to your problem.
See, jQuery basically has two purposes:
Selecting one or more DOM elements from your HTML page
manipulate the selected elements in some way
This can be repeated multiple times, since jQuery functions are chainable (this means you can call function after function after function...).
If I understood your problem correctly, you are trying to build a list of blog posts and only display teasers of them.
After the user clicks the "read more" button, the complete article gets expanded.
Keep in mind: jQuery selects your elements very much like CSS would do. This makes it extremely easy to
come up with a query for certain elements, but you need to structure your HTML in a good way, like
you would do for formatting reasons.
So I suggest you should use this basic markup for each of your articles (heads up, HTML5 at work!):
<article class="article">
<section class="teaser">
Hey, I am a incredible teaser text! I just introduce you to the article.
</section>
<section class="full">
I am the articles body text. You should not see me initially.
</section>
</article>
You can replace the article and section elements with div elements if you like to.
And here is the CSS for this markup:
/* In case you want to display multiple articles underneath, separate them a bit */
.article{
margin-bottom: 50px;
}
/* we want the teaser to stand out a bit, so we format it bold */
.teaser{
font-weight: bold;
}
/* The article body should be a bit separated from the teaser */
.full{
padding-top: 10px;
}
/* This class is used to hide elements */
.hidden{
display: none;
}
The way we created the markup and CSS allows us to put multiple articles underneath.
Okay, you may have noticed: I completely omitted any "read more" or "collapse" buttons. This is done by intention.
If somebody visits the blog site with javascript disabled (maybe a search engine, or a old mobile which doesn't support JS or whatever),
the logic would be broken. Also, many text-snippets like "read more" and "collapse" are not relevant if they don't actually do anything and are not part of the article.
Initially, no article body is hidden, since we didn't apply the hidden css class anywhere. If we would
have embedded it in the HTML and someone really has no JavaScript, he would be unable to read anything.
Adding some jQuery magic
At the bottom of the page, we are embedding the jQuery library from the google CDN.
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
This is a best practice and will normally speed up your page loading time. Since MANY websites are embedding
jQuery through this URL, chances are high that its already in the visitors browser cache and doesn't have
to be downloaded another time.
Notice that the http: at the beginning of the URL is omitted. This causes browsers to use the pages current protocol,
may it be http or https. If you would try and embed the jQuery lib via http protocol on a https website, some browsers will refuse to download the file from a unsecure connection.
After you included jQuery into the page, we are going to add our logic into a script tag. Normally we would
save the logic into a separate file (again caching and what not all), but this time a script block will do fine.
Finally some JavaScript
At first, we want to hide all elements with the css-class full, since only teasers should remain displayed. This is very easy with jQuery:
$('.full').hide();
The beginning of the script $('.full') tells jQuery: I need all elements with the CSS-class full. Then we call a function on that result, namingly hide() which purpose should be clear.
Okay, in the next step, we want to add some "read more" buttons, next to every teaser. Thats an easy task, too:
$('.teaser').after('<button class="more">Read more</button>');
We now select every element with the css-class teaser and append some HTML code after() each element - a button with the css-class more.
In the next step, we tell jQuery to observe clicks on every one of this freshly created buttons. When a user has clicked, we want to expand the next element with the css-class full after the clicked button.
$('.more').on('click', function(){
//"this" is a reference to the button element!
$(this).slideUp().next('.full').slideDown();
});
Phew, what did we do here?
First, we told jQuery that we wanted to manipulate this, which is a reference to the clicked button. Then we told
jQuery to hide that button (since its not needed anymore) slowly with slideUp().
We immediately continued telling jQuery what to do: Now take the next() element (with the css-class full) and make it visible by sliding it down with slideDown().
Thats the power of jQuerys chaining!
Hiding again
But wait, you wanted to be able to collapse the articles again! So we need a "collapse" button, too and
some more JavaScript:
$('.full').append('<button class="collapse">Collapse text</button>');
Note: we didn't use the after() function to add this button, but the append() function to place the button
INSIDE every element with the css-class full, rather than next to it. This is because we want the
collapse buttons to be hidden with the full texts, too.
Now we need to have some action when the user clicks one of those buttons, too:
$('.collapse').on('click', function(){
$(this).parent().slideUp().prev('.more').slideDown();
});
Now, this was easy: We start with the button element, move the focus to its parent() (which is the element that contains the full text) and tell jQuery to hide that element by sliding it up with slideUp().
Then we move the focus from the full-text container to its previous element with the css-class more, which is its expanding button that has been hidden when expanding the text. We slowly show that button again by calling slideDown().
Thats it :)
I've uploaded my example on jsBin.
I've got the following list item:
<li>
<input value="someRadioButton" name="ctl00$mainContent$group" type="radio"
id="ctl00_mainContent_somelRadioButton" onclick="showSomeInfo()" />
<label for="ctl00_mainContent_someRadioButton">
<img class="extraPadding-Right-10" src="https://xxy.com/some_mark_37x23.gif" />
</label>
</li>
So what shows up is a radio button and an image next to it. When I am in FireFox, Chrome, and Safari clicking on that image fires the showSomeInfo() that's specified in the radio's onclick. I'm not sure why I guess because it's wrapped in a label and that label is relating to that radio button....
But anyway that's not my problem. I like that when you click the image, that javascript method showSomeInfo() is called. But the problem is that it works in all browsers except IE 8. If I open this page in IE 8, clicking on the image does nothing and I'm not sure why. I'm baffled at this one.
I was looking for an answer to this and wrote a quick dirty jquery handler for it:
$("label").click(function(){
if ($(this).attr("for") != "")
$("#" + $(this).attr("for")).click();
});
There's a slightly cleaner approach to change the markup that doesn't involve (ugly) CSS hacks or Javascript; change the <img> tag to a <span> tag with a background-image of the appropriate size. For example:
<li>
<input value="someRadioButton" name="ctl00$mainContent$group" type="radio"
id="ctl00_mainContent_somelRadioButton" onclick="showSomeInfo()" />
<label for="ctl00_mainContent_someRadioButton">
<span class="extraPadding-Right-10" style="background-image: url(https://xxy.com/some_mark_37x23.gif); width: 100px; height: 100px; display: inline-block" />
</label>
</li>
Replacing the width and height of your image appropriately.
The reason it works the way it does in Firefox et al is that that's what <label> is supposed to do when it's got a "for" attribute that points to an input field (by "id" value). If it doesn't work that way in IE, it's because Microsoft either interpreted the standard differently or simply failed to implement it correctly. (I don't see any clear language in the w3c reference I found to stipulate whether anything but text content of a label should get focus and transfer it.)
I've also discovered that if you have a hidden input with display:none or visibility:hidden and are relying on the label for selection it will not work in ie8.
My workaround was to have an overflow:hidden on the containing element and position the input outside of this area.
I Guess I have a better hack solution for this problem.
I will explain why for first.
Using jquery to hit the label click works, but not when you are using an Input File, because you will receive access denied.
Using css and display the image as a background it´s not good too because you need to have an image with the exactly size, which is not the case when the user uploads the Image or you have a lot of images with different sizes.
Ok now I´ll explain the idea of the hack:
You have a Label with an image inside, when you click the image, IE8 doesn´t fire the Label. But if you write some text into the Label, when you click the text IE8 fire the label.
The Idea is to put a span inside the label, with the size of the label (width and height)
Ok but if you don´t have text inside it won´t work, but if you change the background color it will work.
So what you need to do is:
Place a Span with the role size of the Label
Write a background color and make it transparent
Position the Span using relative position to put the Span exactly over the Label.
It´s a little hard to do it, but I guess it will cover many more situations.
An example of a Label firing the Input type File:
<label for="" id="lblInput" style="cursor: pointer; z-index:3;">
<img src="../Imagens/SemPerfil.jpg" ID="imgFoto" Style="max-width: 280px; max-height: 180px;z-index:2;" />
<span style="position:relative;
top:-180px;display:block;height:180px;width:280px;
z-index:1;background-color:Aqua;
-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=1)';
background: rgba(255, 255, 255, 0.0);" >
</span>
</label>
I hope it works and save you Like it saved-me
Are you sure you aren't simply looking at a typo?
Check "ctl00_mainContent_someRadioButton" vs. "ctl00_mainContent_somelRadioButton"
It looks like IE doesn't properly handle labels with img elements in them. The only reasonable ways I have been able to find for dealing with this problem are either using HTML component behaviors, Javascript handlers, or CSS hacks. I haven't tried all of these so I can't guarantee that they will work.
I tried some of the other solutions posted here and eventually amended them to get a working fix to your problem. My problem was similar, though the HTML looked a little different, with the LABEL tag wrapping both the image and the INPUT radio button.
The solution I include below will ensure that any onClick handlers fire correctly on the radio button itself. It also does this only once and not in browsers where the radio button label behaves normally.
This is the jQuery code I used to solve the problem in Internet Explorer 11:
$('LABEL > IMG').click(function () {
var inputId = $(this).parents('LABEL:first').attr("for");
if (inputId) $('#' + inputId).not(':checked').attr('checked', true).click();
});
The code above uses the jQuery method attr() to manipulate the checked property. If you're using jQuery 1.6 or higher you should modify this to use the jQuery method prop() instead.
This works for me with a group of radio inputs followed by labels containing an image. Basically used it instead of using for attribute in all browsers
inputs.each(function() {
$(this).click(function (e) {
// do stuff ...
});
// adding click handler to label containing image
$(this).next().on('click', function(event) {
// prevent 'for' attribute firing
event.preventDefault();
// check and trigger click on input
$(this).prev().prop("checked", true).trigger('click');
});
});