Repository: bokeh/bokeh

Issue:
Improve responsiveness when hovering over many points
#### My Pain

I would like to use Bokeh plots in my applications but sometimes they feel slow and sluggish.

An example of this is plots with many points. The many points by them selves is not the main problem. The main problem is hovering.

If the users hovers over the Bokeh plot it becomes unresponsive and my fan turns on. 

I suspect its due to collecting and showing tool tips for many points

My fear is that my users turn away from my apps because they appear slow or dead.

#### Example

https://ferret.pmel.noaa.gov/osmc/dashboard

I hover and the drag the plot. The first time its OK responsive. The second time it takes 10 secs before the plot moves.

![bokeh-slow](https://user-images.githubusercontent.com/42288570/85915787-ce82f100-b84a-11ea-893f-cbb81a084f73.gif)

#### Another Example

Its the same problem at awesome-panel.org in the Kickstarter Dashboard. If I want to select some points to filter on the hovering often steps in first and I have to wait 5-10 secs for the plot to respond.

![image](https://user-images.githubusercontent.com/42288570/85915794-f1ada080-b84a-11ea-996a-5de131f1b3c9.png)

#### Solution

Fix this :-) For example by 

- fixing the cause if there is a cause or
- setting a max on the number of points to show hover information for.

FYI @philippjfr 


Discussion:

Resolving patch:
diff --git a/bokeh/models/annotations.py b/bokeh/models/annotations.py
--- a/bokeh/models/annotations.py
+++ b/bokeh/models/annotations.py
@@ -1048,6 +1048,10 @@ class Tooltip(Annotation):
 
     inner_only = Bool(default=True, help="""
     Whether to display outside a central plot frame area.
+
+    .. note:
+        This property is deprecated and will be removed in bokeh 3.0.
+
     """)
 
     show_arrow = Bool(default=True, help="""


Test patch:
diff --git a/bokehjs/test/unit/core/util/eq.ts b/bokehjs/test/unit/core/util/eq.ts
--- a/bokehjs/test/unit/core/util/eq.ts
+++ b/bokehjs/test/unit/core/util/eq.ts
@@ -156,6 +156,35 @@ describe("core/util/eq module", () => {
     expect(is_equal(x2, x0)).to.be.false
   })
 
+  it("that supports Node instances", () => {
+    const text0 = document.createTextNode("abc def")
+    const text1 = document.createTextNode("abc def")
+    const text2 = document.createTextNode("ABC DEF")
+    const span0 = document.createElement("span")
+    const div0 = document.createElement("div")
+    const div1 = document.createElement("div")
+    div1.textContent = "abc def"
+    const div2 = document.createElement("div")
+    div2.appendChild(div1)
+
+    expect(is_equal(text0, text0)).to.be.true
+    expect(is_equal(text0, text1)).to.be.true
+    expect(is_equal(text0, text2)).to.be.false
+    expect(is_equal(text0, span0)).to.be.false
+    expect(is_equal(text0, div0)).to.be.false
+    expect(is_equal(text0, div1)).to.be.false
+    expect(is_equal(text0, div2)).to.be.false
+
+    expect(is_equal(span0, div0)).to.be.false
+
+    expect(is_equal(div0, div0)).to.be.true
+    expect(is_equal(div0, div1)).to.be.false
+    expect(is_equal(div0, div2)).to.be.false
+    expect(is_equal(div1, div2)).to.be.false
+    expect(is_equal(div1, div1)).to.be.true
+    expect(is_equal(div2, div2)).to.be.true
+  })
+
   it("that throws on unknown types", () => {
     class X {
       constructor(readonly f: number) {}
diff --git a/bokehjs/test/unit/core/util/templating.ts b/bokehjs/test/unit/core/util/templating.ts
--- a/bokehjs/test/unit/core/util/templating.ts
+++ b/bokehjs/test/unit/core/util/templating.ts
@@ -180,16 +180,22 @@ describe("templating module", () => {
 
     it("should replace field names with values as-is with safe format", () => {
       const s1 = tmpl.replace_placeholders("stuff @foo{safe}", source, 0)
-      expect(s1).to.be.equal("stuff 10")
+      const n1 = document.createTextNode("stuff 10")
+      expect(s1).to.be.equal([n1])
 
       const s2 = tmpl.replace_placeholders("stuff @foo{safe}", source, 1)
-      expect(s2).to.be.equal("stuff 1.002")
+      const n2 = document.createTextNode("stuff 1.002")
+      expect(s2).to.be.equal([n2])
 
       const s3 = tmpl.replace_placeholders("stuff @bar{safe}", source, 0)
-      expect(s3).to.be.equal("stuff a")
+      const n3 = document.createTextNode("stuff a")
+      expect(s3).to.be.equal([n3])
 
       const s4 = tmpl.replace_placeholders("stuff @bar{safe}", source, 1)
-      expect(s4).to.be.equal("stuff <div>b</div>")
+      const n4_0 = document.createTextNode("stuff ")
+      const n4_1 = document.createElement("div")
+      n4_1.textContent = "b"
+      expect(s4).to.be.equal([n4_0, n4_1])
     })
 
     it("should ignore extra/unused formatters", () => {