Using xlink:href in SVG can cause pointless style recalculations
Edited by @evancz
The runtime behavior of justCirc and symAndUse is very different in the following code.
- With
symAndUsethe browser is recalculating styles on every animation frame. - With
justCircit does not do this.
You can observe this by profiling the example with the Chrome timeline
import Html exposing (Html)
import Svg exposing (..)
import Svg.Attributes exposing (..)
import VirtualDom exposing (attributeNS)
import AnimationFrame
import Time exposing (Time)
-- PROBLEM
view : Model -> Html Msg
view model =
let
justCirc = [ circle [cx "50", cy "50", r "40", strokeWidth "8", stroke "red", fill "red"] [] ]
symAndUse =
[ symbol [id "sym01"] justCirc
, use [xlinkHref "#sym01", x "0", y "0", width "100", height "100"] []
]
in
svg [ viewBox "0 0 200 200", width "600px" ]
--justCirc
symAndUse
-- SETUP
main = Html.program { init = init, view = view, update = update, subscriptions = subscriptions }
type alias Model = Time
init : (Model, Cmd Msg)
init =
(0, Cmd.none)
type Msg = Tick Time
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
Tick newTime -> (0, Cmd.none)
subscriptions : Model -> Sub Msg
subscriptions model =
AnimationFrame.times Tick
Original Comment:
If you profile this example using the Chrome timeline, you can see that the browser is recalculating styles on every animation frame. Since the DOM isn't changing at all, this shouldn't be the case? If you were to replace the last line
symAndUsewithjustCirc, it does work as expected (no calculating restyles).
Thanks for the issue! Make sure it satisfies this checklist. My human colleagues will appreciate it!
Here is what to expect next, and if anyone wants to comment, keep these things in mind.
I believe the reason for this issue is that for all namespaced attributes the value is an object,
https://github.com/elm/virtual-dom/blob/5a5bcf48720bc7d53461b3cd42a9f19f119c5503/src/Elm/Kernel/VirtualDom.js#L263-L270
which we then try to compare by reference.
https://github.com/elm/virtual-dom/blob/5a5bcf48720bc7d53461b3cd42a9f19f119c5503/src/Elm/Kernel/VirtualDom.js#L911-L913
This is bound to fail and force a patch every time we do a comparison on namespaced attributes.
It seems like Firefox is kinder when you use setAttributeNS with the old value, while Chrome forces a recalculation and will even try to re-fetch images from the network (see evancz/elm-playground#5 for an example).
Adding a condition like this
if (xValue === yValue && xKey !== 'value' && xKey !== 'checked'
|| category === 'a__1_EVENT' && _VirtualDom_equalEvents(xValue, yValue)
|| category === 'a__1_ATTR_NS' && xValue.__namespace === yValue.__namespace && xValue.__value === yValue.__value)
to the if-clause in _VirtualDom_diffFacts should solve the problem.