I'm working on a project that approximates the functionality of Firebug's inspector tool. That is, when mousing over elements on the page, I'd like to highlight them (by changing their background color), and when they're clicked, I'd like to execute a function that builds a CSS selector that can be used to identify them.
However, I've been running into problems related to event bubbling, and have thoroughly confused myself. Rather than walk you down that path, it might make sense just to explain what I'm trying to do and ask for some help getting started. Here are some specs:
I'm only interested in elements that contain a text node (or any descendant elements with text nodes).
When the mouse enters such an element, change its background color.
When the mouse leaves that element, change its background color back to what it was originally.
When an element is clicked, execute a function that builds a CSS selector for that element.
I don't want a mouseover on an element's margin area to count as a mouseover for that element, but for the element beneath (I think that's default browser behavior anyway?).
I can handle the code that highlights/unhighlights, and builds the CSS selector. What I'm primarily having trouble with is efficiently binding event handlers to the elements that I want to be highlightable/clickable, and avoiding/stopping bubbling so that mousing over a (<p>) element doesn't also execute the handler function on the <body>, for example. I think the right way to do this is to bind event handlers to the document element, then somehow use bubbling to only execute the bound function on the topmost element, but I don't have any idea what that code looks like, and that's really where I could use help.
I'm using jQuery, and would like to rely on that as much as possible.
You can add an event listeners to document.body. The handler function can inspect the event to figure out which element was originally targeted. If you were using the Prototype library, you would use this:
http://prototypejs.org/api/event/element
I know you're using jQuery, but I'm sure it has equivalent functionality; I'm just not as familiar with it.
This is going to be fairly problematic, because you can't directly style the text nodes in your document. That means that if you've got something like this:
<div>
Hello world how are you
<ul>
<li>First of all, it is a lovely day outside</li>
<li>Second, it's important that you
<a href='http://somewhere.else'>click here</a>
</li>
</ul>
</div>
Well when you try to restyle the text block at the head of that <div>, you'll need to do it in such a way that the rest of the <div> doesn't also get a new background color. (At least, I think that's what you're asking for.)
There's a "highlight" plugin for jQuery that you might look at as a guide to how you can replace simple text nodes with <span> tags that have some given class. The plugin is intended to let you style words/phrases that you search for, but it's not terribly complicated and you might be able to adapt it. The plugin is here: http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html
Related
I have an element with a text that shows a tooltip when the text is truncated. I want to remove the tooltip when the element's text is no longer truncated. The problem is I can't get to the event of the tooltip. I add the tooltip by setting the attribute to the element on truncation. However, removing the attribute doesn't remove the event and the tooltip still shows up. I am using Angular bootstrap tooltip.
I searched the internet and removeEventListener() didn't help because I don't have the tooltip event handler. The only workaround I was able to use and worked is triggering the event mouseleave on the element which hid the tooltip, not removed it. I think this is not a good way of doing it, I need to remove that event.
By the way, I am using angular and javascript only, no jQuery.
Any ideas how to do this?
Edit:
My element is like this:
<span>Here goes the text</span>
and after adding the tooltip the element looks like this
<span uib-tooltip="Here goes the text" tooltip-append-to-body="true" tooltip-placement="bottom">Here goes the text</span>
You should provide tooltip-enable expression in attributes which will return if text is truncated or not. This can be easily determined like so: https://stackoverflow.com/a/143889/2337927. Keep in mind though that you can't use DOM elements directly in angular expressions due to security issues. What you want to do is for example register isTextOverflowing function on controller and call it from the expression: tooltip-enable="vm.isTextOverflowing()"
Edit
If you are positive that what you want is to remove event listener completely then I'm afraid that will involve writing a decorator but in really messy (or even hacky) way but if what you're up to is simply closing the tooltip when text becomes fully visible it's easy to accomplish using combination of tooltip-enable and tooltip-is-open such as shown in this plunk.
It is simply possible to find all truly visible and clickable elements in the page using the document.elementFromPoint function. However, it returns null for elements outside of the viewport.
So, how to find all clickable and visible elements in the full page? The visible elements are not just limited to the styles. Just consider a container <div> which is now hidden behind all children elements. So, the parent <div> is not longer visible.
So, do you have any idea how it is possible to find all really visible elements in the page? In the example above, obviously “Parent <div>” is not visible practically. There are some other unpredictable situations where those elements may not be visible and the styles (display,visibility, etc.) may not indicate it.
My final intention: I want to check if an element is really visible and clickable for the end-user or not. As an example use case I want to find all possible zones a user may click on.
I think you've misunderstand the basics behind events in domApi.
there will be bubbles and captures in any event happened at the client.
you must have seen code like this document.addEventListener('click',function(){},false),that means to use bubble instead of capture to handle event.
so actually clickable dom element is related with whether or not DOMJs uses bubble or capture
Is there a way to determine which element is beneath a given element on a page? I have a tool that I would like to use over several elements which are visible only one at a time. I'd like to be able to determine which element is visible under that given element. Can this be done?
There is no way to reliably do this natively in JavaScript. CSS can effect the layout in ways that you can not predict.
I can think of one solution where you find all the elements on the page and their offsets and then try to work back from that, but that wont perform very well I suspect.
You can get the immediately following sibling (in a set of matched elements) with the jQuery .next() function:
http://api.jquery.com/next/
Depending on the use case I can think of several options.
One option would be triggering a click event onto the container and then selecting the event.target in the event handler. It should be the element on top.
like this:
http://jsfiddle.net/sm7gH/
My question was about finding the element closet to a point or an element. This plug-in does this:
jquery nearest.
I want to create a little WYSIWYG editor.
The idea:
First I want to add the feature to write and change text. So I add an onClick and onKeyBoard Listener to my div container. When I click the div I set a varaible named "focused" to true. When an key event is fired I check if focused is true. In case focus is false nothing will happen else the new charater will be added on the cursor's position.
My questions:
Is this the right way? I tried to check how other editors handle the text input but I wasnt able to get it.
In case this is the right way - how can I simulate a blinking cursor. In a textarea the cursor will blink but who about a div container? The cursor will hide immideatly after clicking.
I'm assuming you're doing this for fun/practice. If you're doing this for professional reason then I HIGHLY recommend you don't reinvent the wheel and use something like Ckeditor, tinyMCE or YUI.
That being said; you need to look into event handling. Specifically, for your question about focusing, you can look here. The way you're describing (setting a variable to true/false) seems like it is going to just run into problems. If you use the standard events attribute (as opposed to setting a "focus" variable onclick) you should define functions to execute and then set them as an onfocus/onblur attribute for the element you're listening to.
That is if you aren't using a javacript library like mootools, jquery, extJS, etc. If you're using one of those they likely have their own way of handling events, so you should search their respective documentation for how to implement event handlers.
One more note; you really should be using a textarea over a div (unless I'm misunderstanding and you just want to do something when a user focuses on your div). If you're using javascript only to completely reinvent a texteditor from a div; then your web page will not function without javascript. If you keep the text area; users could still type information in and you still get the benefit of grabbing text contents for form submits but using divs means your web page will just be rendered useless without javascript.
I'm currently experiencing click events intermittently not firing. Anyone else ever had this problem?
Code is simple:
<ul class="iconButtons ui-widget ui-helper-clearfix">
<li class="ui-state-default ui-corner-all" title="Save">
<span class="btnSave ui-icon ui-icon-disk"></span>
</li>
</ul>
$(document).ready(function() {
$(".btnSave").click(function() {
alert("Sometimes I never get called!");
});
});
Occurs frequently in all browsers. Using live demonstrates the same behaviour.
I would venture to say that there is some other complication going on to prevent what you are doing.
Here are some possibilities:
Unless you give that empty span display:block; then on some browsers it will have a width and height of 0px and be unclickable. Keep in mind just adding width and height to a span won't actually work on inline elements.
You are ajax'ing content in, and not rebinding the click handler. You can check at any time by doing $(".btnSave").data("events") in your firebug or chrome console to see the number of events to that element.
Another event is usurping your event, using the technique in #2 may help reveal this.
Your click handle is being called, but not returning the right result causing to believe it wasn't being called. Have you tried adding an alert('called') to the very top of the click handler?
Are you certain the element exists in the DOM prior to appending the click element to it? You can check by doing an alert($(".btnSave").length) at the line JUST before you bind the click handler.
I would suggest you use an anchor instead of a span for your button it will fire for sure.
Put
$(".btnSave").click(function(event){
event.preventDefault();
alert("Clicked");
});
In IE, you also have to have content inside an anchor for it to work: background image / background color/ text (maybe also with big negative text-indent)
Your code will hook up event handlers to all elements with that class that already exist when the code is called. If you add more later, they won't get the handler because, well, you haven't asked that they do. :-) Options:
You could use live instead, if you add and remove these elements dynamically. live (and the related delegate) use event delegation to watch for events rather than actually attaching the handlers to the elements in question. live uses the document itself. Since click bubbles, document sees all clicks (that aren't cancelled), and so jQuery's document-wide handler can see if the click was on a .btnSave element and fire your handler if so.
You could put your script at the bottom of the page (just before the closing </body> element), so that all of the elements are there when you hook up your handler.
You could use jQuery's ready function to ensure the DOM is ready before you hook up your handlers.
Alternately, as quoted your span is pretty darned hard to click on (what with being completely empty) unless there's some CSS giving it dimensions you haven't shown... ;-)
Update: You've said the span has dimensions, and that the handler is being hooked up fine (you didn't say how you know that). The only thing left is if something is hooking the click event on those elements and cancelling them (e.g., via stopImmediatePropagation, like this), and it happens that they're earlier in the event handler list than your handler is. It seems more likely that there's an issue hooking things up, though.
There may be many different reasons for that, eg.:
the JS code you are referring to is not executed correctly (does not bind the event in the correct moment in time), try executing it when the DOM is ready:
jQuery(function(){
// your code goes here
});
you may be creating this element dynamically (if you bind it first, then create element, then this element will not have the specific event). The solution is to use .delegate() or .live() jQuery functions.
the event may be unbound somewhere in your code. Try searching for usage of .unbind() jQuery's function within JS code (or even HTML).
It turns out the span which the click event was being added to only occupied the central part of the button's graphic. Clicking directly on the glyph always fired the event, but clicking slightly outside (although seemingly still inside the button) would not raise the event.
I hope this helps anyone else using mini JQuery buttons in the same way they are presented on the JQuery UI ThemeRoller page.
I'm running jquery-ui-1.10.3 and I'm having the same intermittent issue with .toggle buttons -- they just aren't very responsive. I think it's inherent in jquery-ui because even on their demo page the toggle button feels less-than-awesome in terms of tactile response. If I click very slowly and deliberately I can usually get the button to toggle on and off but fast clicking is very hit or miss. I've tried all the tips to speed up jquery-ui but none have worked.