You are viewing a plain text version of this content. The canonical link for it is here.
Posted to batik-users@xmlgraphics.apache.org by Age Bosma <ag...@gmail.com> on 2009/01/14 10:04:10 UTC

Determine clicked 'use' element

Hi,

Is there a way to determine which 'use' element is clicked? The only
thing I'm getting back all the time is the referenced element.

The svg file:
----------------------------------------------------------------------------
<svg ... >
    <g id="device">
        <rect id="rectxx" ... />
        <text ... /> ... </text>
    </g>
    <use id="used_device" xlink:href="#device" ... />
</svg>
----------------------------------------------------------------------------

Attach the listener to the document root:
----------------------------------------------------------------------------
SVGSVGElement svgRoot = svgDoc.getRootElement();

EventTarget et = (EventTarget) svgRoot;
et.addEventListener("click", new DeviceHandler(), false);
----------------------------------------------------------------------------

Listener class:
----------------------------------------------------------------------------
public class DeviceHandler implements EventListener {
    public void handleEvent(Event evt) {
        SVGElement targetElement = (SVGElement) evt.getTarget();
        SVGElement parentElement = (SVGElement) targetElement.getParentNode();

        System.out.println("Target id: no clue...");
        System.out.println("Referenced id: " + targetElement.getId());
        System.out.println("Parent id:" + parentElement.getId());
    }
}
----------------------------------------------------------------------------

The reason I'm attaching the listener to the document root is because
the are quite a few elements of which I want to determine if someone
clicked on it.
getTarget() Always gives me the referenced element if someone clicked
on the 'use' element and thus e.g. the id of the rect. This is fine if
someone actually clicked on the 'rect' element but I'd like to know
which 'use' element was clicked (the id of the use element) if someone
clicked on that.
Using getCurrentTarget() instead of getTarget() always gives me the
'svg' element, so that doesn't appear to be useful either.

How should one determine which 'use' element is clicked on?

Yours,

Age Bosma

---------------------------------------------------------------------
To unsubscribe, e-mail: batik-users-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: batik-users-help@xmlgraphics.apache.org


Re: Determine clicked 'use' element

Posted by Dan Slater <ds...@simulsoft.com>.
Cameron McCormack wrote:
> Hi Age.
>
> Age Bosma:
>   
>> There's one problem with the method you provided though. You are using
>> the 'SVGOMCSSImportedElementRoot' class but this one isn't part of
>> batik at all? As far as I have been able to determine it was included
>> in version 1.5.1 but it has been removed in a later release for some
>> reason. Is there a replacement of some sort available?
>>     
>
> In more recent versions those methods were moved to the CSSNavigableNode
> interface.
>
>   
I'm currently using Batik v1.6, thanks for the heads up. - Dan

---------------------------------------------------------------------
To unsubscribe, e-mail: batik-users-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: batik-users-help@xmlgraphics.apache.org


Re: Determine clicked 'use' element

Posted by Cameron McCormack <ca...@mcc.id.au>.
Hi Age.

Age Bosma:
> There's one problem with the method you provided though. You are using
> the 'SVGOMCSSImportedElementRoot' class but this one isn't part of
> batik at all? As far as I have been able to determine it was included
> in version 1.5.1 but it has been removed in a later release for some
> reason. Is there a replacement of some sort available?

In more recent versions those methods were moved to the CSSNavigableNode
interface.

-- 
Cameron McCormack ≝ http://mcc.id.au/

---------------------------------------------------------------------
To unsubscribe, e-mail: batik-users-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: batik-users-help@xmlgraphics.apache.org


Re: Determine clicked 'use' element

Posted by Age Bosma <ag...@gmail.com>.
2009/1/29 Dan Slater <ds...@simulsoft.com>:

> Hi, Age,
>
> Never mind the filter.  This will get you where you want to go...supply your
> click target, and modify it to break at the use element - you'll see you can
> access the id:
>
>   public static SVGElement returnNextDOMEltUp(SVGElement svgElt) {
>       Node n = svgElt;
>       String name = "start";
>
>       while (!name.equals("g")) {
>               if (n instanceof SVGOMCSSImportedElementRoot){
>                   n=((SVGOMCSSImportedElementRoot)n).getCSSParentElement();
>               } else    {
>                   n = n.getParentNode();
>                   if (n instanceof SVGOMDocument) return null;
>               }
>               if (n instanceof SVGElement) {
>                   showIdAndNodeName(n);
>               }
>               name = n.getNodeName();
>       }
>             if (n instanceof SVGElement) {
>               svgElt = (SVGElement)n;
>           } else svgElt = null;
>             return svgElt;
>   }
>

Thanks for helping out Dan. I never expected it to require such
trickery but if it works it works.
There's one problem with the method you provided though. You are using
the 'SVGOMCSSImportedElementRoot' class but this one isn't part of
batik at all? As far as I have been able to determine it was included
in version 1.5.1 but it has been removed in a later release for some
reason. Is there a replacement of some sort available?

Yours,

Age

---------------------------------------------------------------------
To unsubscribe, e-mail: batik-users-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: batik-users-help@xmlgraphics.apache.org


Re: Determine clicked 'use' element

Posted by Dan Slater <ds...@simulsoft.com>.
Hi, Age,

You're welcome, I'm glad something I did is of use to others. 

To understand why it works, imagine that the use element is like a 
phantom svg tree hanging off your DOM tree.  If you just climb the use 
element you'll eventually hit an empty svg, like the hook on a tree 
ornament!  Just comment out the .getCSSParent() call, you'll see.  
That's the imported root, I think of it as imported from <defs>.  So, if 
you hit an imported svg, you have to step off to the branch of the DOM 
tree to continue climbing, and the Batik folks have given us this method 
for doing so.  I suggest reading the W3C spec on <use> elements very 
closely - I found it somewhat ambiguous.

Your suggestion regarding the null value is well taken, but the behavior 
is a design choice (maybe not the best) for consistency with 
DOMTreeWalker navigation behavior.  Sometimes my code starts from a 
group just under the document element and never sees a <use> element.

Dan

Age Bosma wrote:
> 2009/1/29 Dan Slater <ds...@simulsoft.com>:
>
>   
>> Hi, Age,
>>
>> Never mind the filter.  This will get you where you want to go...supply your
>> click target, and modify it to break at the use element - you'll see you can
>> access the id:
>>
>>   public static SVGElement returnNextDOMEltUp(SVGElement svgElt) {
>>       Node n = svgElt;
>>       String name = "start";
>>
>>       while (!name.equals("g")) {
>>               if (n instanceof SVGOMCSSImportedElementRoot){
>>                   n=((SVGOMCSSImportedElementRoot)n).getCSSParentElement();
>>               } else    {
>>                   n = n.getParentNode();
>>                   if (n instanceof SVGOMDocument) return null;
>>               }
>>               if (n instanceof SVGElement) {
>>                   showIdAndNodeName(n);
>>               }
>>               name = n.getNodeName();
>>       }
>>             if (n instanceof SVGElement) {
>>               svgElt = (SVGElement)n;
>>           } else svgElt = null;
>>             return svgElt;
>>   }
>>
>>     
>
> Many thanks for the help Dan, with some minor alterations it works
> like a charm. I still don't completely understand why it has to be
> done this way but it works.
>
> One alteration worth mentioning is changing the 'if (n instanceof
> SVGOMDocument)' statement to return the original input element
> 'svgElt' instead of null. This will deal with referenced elements not
> located in a 'defs' element/section.
>
> Age
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: batik-users-unsubscribe@xmlgraphics.apache.org
> For additional commands, e-mail: batik-users-help@xmlgraphics.apache.org
>
>
>   


---------------------------------------------------------------------
To unsubscribe, e-mail: batik-users-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: batik-users-help@xmlgraphics.apache.org


Re: Determine clicked 'use' element

Posted by Age Bosma <ag...@gmail.com>.
2009/1/29 Dan Slater <ds...@simulsoft.com>:

> Hi, Age,
>
> Never mind the filter.  This will get you where you want to go...supply your
> click target, and modify it to break at the use element - you'll see you can
> access the id:
>
>   public static SVGElement returnNextDOMEltUp(SVGElement svgElt) {
>       Node n = svgElt;
>       String name = "start";
>
>       while (!name.equals("g")) {
>               if (n instanceof SVGOMCSSImportedElementRoot){
>                   n=((SVGOMCSSImportedElementRoot)n).getCSSParentElement();
>               } else    {
>                   n = n.getParentNode();
>                   if (n instanceof SVGOMDocument) return null;
>               }
>               if (n instanceof SVGElement) {
>                   showIdAndNodeName(n);
>               }
>               name = n.getNodeName();
>       }
>             if (n instanceof SVGElement) {
>               svgElt = (SVGElement)n;
>           } else svgElt = null;
>             return svgElt;
>   }
>

Many thanks for the help Dan, with some minor alterations it works
like a charm. I still don't completely understand why it has to be
done this way but it works.

One alteration worth mentioning is changing the 'if (n instanceof
SVGOMDocument)' statement to return the original input element
'svgElt' instead of null. This will deal with referenced elements not
located in a 'defs' element/section.

Age

---------------------------------------------------------------------
To unsubscribe, e-mail: batik-users-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: batik-users-help@xmlgraphics.apache.org


Re: Determine clicked 'use' element

Posted by Dan Slater <ds...@simulsoft.com>.
Dan Slater wrote:
> Age Bosma wrote:
>> 2009/1/29  <th...@kodak.com>:
>>  
>>> Hi Age,
>>>
>>> Age Bosma <ag...@gmail.com> wrote on 01/28/2009 02:43:51 PM:
>>>
>>>    
>>>> Age Bosma wrote:
>>>>
>>>>      
>>>>> getTarget() Always gives me the referenced element if someone clicked
>>>>> on the 'use' element and thus e.g. the id of the rect. This is 
>>>>> fine if
>>>>> someone actually clicked on the 'rect' element but I'd like to know
>>>>> which 'use' element was clicked (the id of the use element) if 
>>>>> someone
>>>>> clicked on that.
>>>>>         
>>>    Well you can walk up the tree from the clicked element to the
>>> use element.  In Batik the elements are all basically normal DOM 
>>> elements
>>> (they should be SVGElementInstance references).  In anycase you should
>>> be able to use 'getParentNode()' (java) or 'parentNode' (ecmascript) to
>>> walk up the tree checking if the element is a use element or not.
>>>
>>>     
>>
>> The problem is that I'm unable to get to a use element. If I click on
>> a use element, I'm getting exactly the same back as when I click on
>> the element the use element is referencing (getNodeName(), getId(),
>> etc.). If you look at the initial e-mail describing the situation,
>> getNodeName() will always be 'rect' even if I click on the use
>> element. The same goes for the Id.
>>
>> A use element does not contain any children, it only references an
>> element to get a clone. getParentNode() is therefor no option to get
>> to the use element.
>>
>> Age
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: batik-users-unsubscribe@xmlgraphics.apache.org
>> For additional commands, e-mail: batik-users-help@xmlgraphics.apache.org
>>
>>
>>   
> Hi, Age,
>
> I think this is really a W3C DOM navigation question but I suppose 
> Batik has something to do with the DOM ;-)  I think you need to climb 
> the DOM  from your target  and 'escape' from the local use element 
> <defs> reference using this test of each Node n (quick and dirty):
>
>                if (n instanceof SVGOMCSSImportedElementRoot){
>                    
> n=((SVGOMCSSImportedElementRoot)n).getCSSParentElement();
>                }
>
> It was a bit of a bear to figure that out and maybe someone could tell 
> me an easier way or better way!
>
> Then test for the custom attribute that Thomas mentioned.  I haven't 
> implemented your exact case, but in a separate part of my app I supply 
> an SVGElement as a DOMTreeWalker root with a filter like so:
>
>    class ReceptorsAll implements NodeFilter {
>
>           public short acceptNode(Node n) {
>                String segtype = ((SVGElement)n).getAttributeNS(null, 
> "segtype");
>                if (n.getNodeType() == Node.ELEMENT_NODE) {
>                    if (segtype.equals("receptor") &&
>                            (n.getNodeName().equalsIgnoreCase("g")
>                            || n.getNodeName().equalsIgnoreCase("use"))
>                        ) {
>                        return FILTER_ACCEPT;
>                    } else return FILTER_SKIP;
>                } else
>                    return FILTER_SKIP;
>           }
>
> As you see, the filter is pretty highly coupled to my DOM structure 
> and looks for the custom attribute.  Then I extract all the id's from 
> the walker's children.
>
> I will probably have to implement some version of what you're doing in 
> the future, so I'd be curious as to your solution.  Hope this helps...
>
> Dan Slater
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: batik-users-unsubscribe@xmlgraphics.apache.org
> For additional commands, e-mail: batik-users-help@xmlgraphics.apache.org
>
>
Hi, Age,

Never mind the filter.  This will get you where you want to go...supply 
your click target, and modify it to break at the use element - you'll 
see you can access the id:

    public static SVGElement returnNextDOMEltUp(SVGElement svgElt) {
        Node n = svgElt;
        String name = "start";

        while (!name.equals("g")) {
                if (n instanceof SVGOMCSSImportedElementRoot){
                    
n=((SVGOMCSSImportedElementRoot)n).getCSSParentElement();
                } else    {
                    n = n.getParentNode();
                    if (n instanceof SVGOMDocument) return null;
                }
                if (n instanceof SVGElement) {
                    showIdAndNodeName(n);
                }
                name = n.getNodeName();
        }
       
        if (n instanceof SVGElement) {
                svgElt = (SVGElement)n;
            } else svgElt = null;
       
        return svgElt;
    }

Cheers!  Dan

---------------------------------------------------------------------
To unsubscribe, e-mail: batik-users-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: batik-users-help@xmlgraphics.apache.org


Re: Determine clicked 'use' element

Posted by Dan Slater <ds...@simulsoft.com>.
Age Bosma wrote:
> 2009/1/29  <th...@kodak.com>:
>   
>> Hi Age,
>>
>> Age Bosma <ag...@gmail.com> wrote on 01/28/2009 02:43:51 PM:
>>
>>     
>>> Age Bosma wrote:
>>>
>>>       
>>>> getTarget() Always gives me the referenced element if someone clicked
>>>> on the 'use' element and thus e.g. the id of the rect. This is fine if
>>>> someone actually clicked on the 'rect' element but I'd like to know
>>>> which 'use' element was clicked (the id of the use element) if someone
>>>> clicked on that.
>>>>         
>>    Well you can walk up the tree from the clicked element to the
>> use element.  In Batik the elements are all basically normal DOM elements
>> (they should be SVGElementInstance references).  In anycase you should
>> be able to use 'getParentNode()' (java) or 'parentNode' (ecmascript) to
>> walk up the tree checking if the element is a use element or not.
>>
>>     
>
> The problem is that I'm unable to get to a use element. If I click on
> a use element, I'm getting exactly the same back as when I click on
> the element the use element is referencing (getNodeName(), getId(),
> etc.). If you look at the initial e-mail describing the situation,
> getNodeName() will always be 'rect' even if I click on the use
> element. The same goes for the Id.
>
> A use element does not contain any children, it only references an
> element to get a clone. getParentNode() is therefor no option to get
> to the use element.
>
> Age
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: batik-users-unsubscribe@xmlgraphics.apache.org
> For additional commands, e-mail: batik-users-help@xmlgraphics.apache.org
>
>
>   
Hi, Age,

I think this is really a W3C DOM navigation question but I suppose Batik 
has something to do with the DOM ;-)  I think you need to climb the DOM  
from your target  and 'escape' from the local use element <defs> 
reference using this test of each Node n (quick and dirty):

                if (n instanceof SVGOMCSSImportedElementRoot){
                    
n=((SVGOMCSSImportedElementRoot)n).getCSSParentElement();
                }

It was a bit of a bear to figure that out and maybe someone could tell 
me an easier way or better way!

Then test for the custom attribute that Thomas mentioned.  I haven't 
implemented your exact case, but in a separate part of my app I supply 
an SVGElement as a DOMTreeWalker root with a filter like so:

    class ReceptorsAll implements NodeFilter {

           public short acceptNode(Node n) {
                String segtype = ((SVGElement)n).getAttributeNS(null, 
"segtype");
                if (n.getNodeType() == Node.ELEMENT_NODE) {
                    if (segtype.equals("receptor") &&
                            (n.getNodeName().equalsIgnoreCase("g")
                            || n.getNodeName().equalsIgnoreCase("use"))
                        ) {
                        return FILTER_ACCEPT;
                    } else return FILTER_SKIP;
                } else
                    return FILTER_SKIP;
           }

As you see, the filter is pretty highly coupled to my DOM structure and 
looks for the custom attribute.  Then I extract all the id's from the 
walker's children.

I will probably have to implement some version of what you're doing in 
the future, so I'd be curious as to your solution.  Hope this helps...

Dan Slater

---------------------------------------------------------------------
To unsubscribe, e-mail: batik-users-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: batik-users-help@xmlgraphics.apache.org


Re: Determine clicked 'use' element

Posted by Age Bosma <ag...@gmail.com>.
2009/1/29  <th...@kodak.com>:
>
> Hi Age,
>
> Age Bosma <ag...@gmail.com> wrote on 01/28/2009 02:43:51 PM:
>
>> Age Bosma wrote:
>>
>> > getTarget() Always gives me the referenced element if someone clicked
>> > on the 'use' element and thus e.g. the id of the rect. This is fine if
>> > someone actually clicked on the 'rect' element but I'd like to know
>> > which 'use' element was clicked (the id of the use element) if someone
>> > clicked on that.
>
>    Well you can walk up the tree from the clicked element to the
> use element.  In Batik the elements are all basically normal DOM elements
> (they should be SVGElementInstance references).  In anycase you should
> be able to use 'getParentNode()' (java) or 'parentNode' (ecmascript) to
> walk up the tree checking if the element is a use element or not.
>

The problem is that I'm unable to get to a use element. If I click on
a use element, I'm getting exactly the same back as when I click on
the element the use element is referencing (getNodeName(), getId(),
etc.). If you look at the initial e-mail describing the situation,
getNodeName() will always be 'rect' even if I click on the use
element. The same goes for the Id.

A use element does not contain any children, it only references an
element to get a clone. getParentNode() is therefor no option to get
to the use element.

Age

---------------------------------------------------------------------
To unsubscribe, e-mail: batik-users-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: batik-users-help@xmlgraphics.apache.org


Re: Determine clicked 'use' element

Posted by th...@kodak.com.
Hi Age,

Age Bosma <ag...@gmail.com> wrote on 01/28/2009 02:43:51 PM:

> Age Bosma wrote:
> 
> > getTarget() Always gives me the referenced element if someone clicked
> > on the 'use' element and thus e.g. the id of the rect. This is fine if
> > someone actually clicked on the 'rect' element but I'd like to know
> > which 'use' element was clicked (the id of the use element) if someone
> > clicked on that.

   Well you can walk up the tree from the clicked element to the 
use element.  In Batik the elements are all basically normal DOM elements
(they should be SVGElementInstance references).  In anycase you should
be able to use 'getParentNode()' (java) or 'parentNode' (ecmascript) to
walk up the tree checking if the element is a use element or not.

   One problem you can run into in the 'general' case is that you don't 
know if it's the use element you really want, so I would suggest adding
a custom attribute that you can check for as you walk up the tree so you
know you have reached your element.

> > Using getCurrentTarget() instead of getTarget() always gives me the
> > 'svg' element, so that doesn't appear to be useful either.
> > 
> > How should one determine which 'use' element is clicked on?

Re: Determine clicked 'use' element

Posted by Age Bosma <ag...@gmail.com>.
Age Bosma wrote:

> getTarget() Always gives me the referenced element if someone clicked
> on the 'use' element and thus e.g. the id of the rect. This is fine if
> someone actually clicked on the 'rect' element but I'd like to know
> which 'use' element was clicked (the id of the use element) if someone
> clicked on that.
> Using getCurrentTarget() instead of getTarget() always gives me the
> 'svg' element, so that doesn't appear to be useful either.
> 
> How should one determine which 'use' element is clicked on?
> 

Does no response mean it's not possible?

Age

---------------------------------------------------------------------
To unsubscribe, e-mail: batik-users-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: batik-users-help@xmlgraphics.apache.org