|
| 1 | +Server Side Search Integration |
| 2 | +============================== |
| 3 | + |
| 4 | +Read the Docs provides :doc:`server side search (SSS) </server-side-search>` |
| 5 | +in replace of the default search engine of your site. |
| 6 | +To accomplish this, Read the Docs parses the content directly from your HTML pages [*]_. |
| 7 | + |
| 8 | +If you are the author of a theme or a static site generator you can read this document, |
| 9 | +and follow some conventions in order to improve the integration of SSS with your theme/site. |
| 10 | + |
| 11 | +Indexing |
| 12 | +-------- |
| 13 | + |
| 14 | +The content of the page is parsed into sections, |
| 15 | +in general, the indexing process happens in three steps: |
| 16 | + |
| 17 | +#. Identify the main content node. |
| 18 | +#. Remove any irrelevant content from the main node. |
| 19 | +#. Parse all sections inside the main node. |
| 20 | + |
| 21 | +Read the Docs makes use of ARIA_ roles and other heuristics in order to process the content. |
| 22 | + |
| 23 | +.. tip:: |
| 24 | + |
| 25 | + Following the ARIA_ conventions will also improve the accessibility of your site. |
| 26 | + See also https://webaim.org/techniques/semanticstructure/. |
| 27 | + |
| 28 | +.. _ARIA: https://www.w3.org/TR/wai-aria/ |
| 29 | + |
| 30 | +Main content node |
| 31 | +~~~~~~~~~~~~~~~~~ |
| 32 | + |
| 33 | +The main content node should have a main role (or a ``main`` tag), and there should only be one per page. |
| 34 | +This node is the one that contains all the page content. Example: |
| 35 | + |
| 36 | +.. code-block:: html |
| 37 | + :emphasize-lines: 10-12 |
| 38 | + |
| 39 | + <html> |
| 40 | + <head> |
| 41 | + ... |
| 42 | + </head> |
| 43 | + <body> |
| 44 | + <div> |
| 45 | + This content isn't processed |
| 46 | + </div> |
| 47 | + |
| 48 | + <div role="main"> |
| 49 | + All content inside the main node is processed |
| 50 | + </div> |
| 51 | + |
| 52 | + <footer> |
| 53 | + This content isn't processed |
| 54 | + </footer> |
| 55 | + </body> |
| 56 | + </html> |
| 57 | + |
| 58 | +Irrelevant content |
| 59 | +~~~~~~~~~~~~~~~~~~ |
| 60 | + |
| 61 | +If you have content inside the main node that isn't relevant to the page |
| 62 | +(like navigation items, menus, or search box), |
| 63 | +make sure to use the correct role or tag for it. |
| 64 | + |
| 65 | +Roles to be ignored: |
| 66 | + |
| 67 | +- ``navigation`` |
| 68 | +- ``search`` |
| 69 | + |
| 70 | +Tags to be ignored: |
| 71 | + |
| 72 | +- ``nav`` |
| 73 | + |
| 74 | +Example: |
| 75 | + |
| 76 | +.. code-block:: html |
| 77 | + :emphasize-lines: 3-5 |
| 78 | + |
| 79 | + <div role="main"> |
| 80 | + ... |
| 81 | + <nav role="navigation"> |
| 82 | + ... |
| 83 | + </nav> |
| 84 | + ... |
| 85 | + </div> |
| 86 | + |
| 87 | +Sections |
| 88 | +~~~~~~~~ |
| 89 | + |
| 90 | +Sections are ``h`` tags, and sections of the same level should be neighbors. |
| 91 | +Additionally, sections should have an unique ``id`` attribute per page (this is used to link to the section). |
| 92 | +All content bellow the section, till the new section will be indexed as part of the section. Example: |
| 93 | + |
| 94 | +.. code-block:: html |
| 95 | + :emphasize-lines: 2-10 |
| 96 | + |
| 97 | + <div role="main"> |
| 98 | + <h1 id="section-title"> |
| 99 | + Section title |
| 100 | + </h1> |
| 101 | + <p> |
| 102 | + Content to be indexed |
| 103 | + </p> |
| 104 | + <ul> |
| 105 | + <li>This is also part of the section and will be indexed as well</li> |
| 106 | + </ul> |
| 107 | + |
| 108 | + <h2 id="2"> |
| 109 | + This is the start of a new section |
| 110 | + </h2> |
| 111 | + <p> |
| 112 | + ... |
| 113 | + </p> |
| 114 | + |
| 115 | + ... |
| 116 | + |
| 117 | + <h1 id="neigbor-section"> |
| 118 | + This section is neighbor of "section-title" |
| 119 | + </h1> |
| 120 | + <p> |
| 121 | + ... |
| 122 | + </p> |
| 123 | + </div> |
| 124 | + |
| 125 | +Sections can be inside till two nested tags (and have nested sections), |
| 126 | +and its immediate parent can contain the ``id`` attribute. |
| 127 | +Note that the section content still needs to be bellow the ``h`` tag. Example: |
| 128 | + |
| 129 | +.. code-block:: html |
| 130 | + :emphasize-lines: 3-11,14-21 |
| 131 | + |
| 132 | + <div role="main"> |
| 133 | + <div class="section"> |
| 134 | + <h1 id="section-title"> |
| 135 | + Section title |
| 136 | + </h1> |
| 137 | + <p> |
| 138 | + Content to be indexed |
| 139 | + </p> |
| 140 | + <ul> |
| 141 | + <li>This is also part of the section</li> |
| 142 | + </ul> |
| 143 | + |
| 144 | + <div class="section"> |
| 145 | + <div id="nested-section"> |
| 146 | + <h2> |
| 147 | + This is the start of a sub-section |
| 148 | + </h2> |
| 149 | + <p> |
| 150 | + With the h tag within two levels |
| 151 | + </p> |
| 152 | + </div> |
| 153 | + </div> |
| 154 | + </div> |
| 155 | + </div> |
| 156 | + |
| 157 | +.. note:: |
| 158 | + |
| 159 | + The title of the first section will be the title of the page, |
| 160 | + falling back to the ``title`` tag. |
| 161 | + |
| 162 | +Other special nodes |
| 163 | +~~~~~~~~~~~~~~~~~~~ |
| 164 | + |
| 165 | +- **Anchors**: If the title of your section contains an anchor, wrap it in a ``headerlink`` class, |
| 166 | + so it won't be indexed as part of the title. |
| 167 | + |
| 168 | +.. code-block:: html |
| 169 | + :emphasize-lines: 3 |
| 170 | + |
| 171 | + <h2> |
| 172 | + Section title |
| 173 | + <a class="headerlink" title="Permalink to this headline">¶</a> |
| 174 | + </h2> |
| 175 | + |
| 176 | +- **Code blocks**: If a code block contains line numbers, |
| 177 | + wrap them in a ``linenos`` or ``lineno`` class, |
| 178 | + so they won't be indexed as part of the code. |
| 179 | + |
| 180 | +.. code-block:: html |
| 181 | + :emphasize-lines: 3-7 |
| 182 | + |
| 183 | + <table class="highlighttable"> |
| 184 | + <tr> |
| 185 | + <td class="linenos"> |
| 186 | + <div class="linenodiv"> |
| 187 | + <pre>1 2 3</pre> |
| 188 | + </div> |
| 189 | + </td> |
| 190 | + |
| 191 | + <td class="code"> |
| 192 | + <div class="highlight"> |
| 193 | + <pre>First line |
| 194 | + Second line |
| 195 | + Third line</pre> |
| 196 | + </div> |
| 197 | + </td> |
| 198 | + </tr> |
| 199 | + </table> |
| 200 | + |
| 201 | +Overriding the default search |
| 202 | +----------------------------- |
| 203 | + |
| 204 | +Static sites usually have their own static index, |
| 205 | +and search results are retrieved via JavaScript. |
| 206 | +In order for Read the Docs to override the default search as expected, |
| 207 | +themes from the supported generators must follow these conventions. |
| 208 | + |
| 209 | +.. note:: |
| 210 | + |
| 211 | + Read the Docs will fallback to the original search in case of an error or no results. |
| 212 | + |
| 213 | +Sphinx |
| 214 | +~~~~~~ |
| 215 | + |
| 216 | +Sphinx's basic theme provides the `static/searchtools.js`_ file, |
| 217 | +which initializes search with the ``Search.init()`` method. |
| 218 | +Read the Docs overrides the ``Search.query`` method and makes use of ``Search.output.append`` to add the results. |
| 219 | +A simplified example looks like this: |
| 220 | + |
| 221 | +.. code-block:: js |
| 222 | +
|
| 223 | + var original_search = Search.query; |
| 224 | +
|
| 225 | + function search_override(query) { |
| 226 | + var results = fetch_resuls(query); |
| 227 | + if (results) { |
| 228 | + for (var i = 0; i < results.length; i += 1) { |
| 229 | + var result = process_result(results[i]); |
| 230 | + Search.output.append(result); |
| 231 | + } |
| 232 | + } else { |
| 233 | + original_search(query); |
| 234 | + } |
| 235 | + } |
| 236 | +
|
| 237 | + Search.query = search_override; |
| 238 | +
|
| 239 | + $(document).ready(function() { |
| 240 | + Search.init(); |
| 241 | + }); |
| 242 | +
|
| 243 | +Highlights from results will be in a ``span`` tag with the ``highlighted`` class |
| 244 | +(``This is a <span class="highlighted">result</span>``). |
| 245 | +If your theme works with the search from the basic theme, it will work with Read the Docs' SSS. |
| 246 | + |
| 247 | +.. _`static/searchtools.js`: https://github.com/sphinx-doc/sphinx/blob/275d9/sphinx/themes/basic/static/searchtools.js |
| 248 | + |
| 249 | +MkDocs |
| 250 | +~~~~~~ |
| 251 | + |
| 252 | +Search on MkDocs is provided by the `search plugin`_, which is included (and activated) by default in MkDocs. |
| 253 | +The js part of this plugin is included in the `templates/search/main.js`_ file, |
| 254 | +which subscribes to the ``keyup`` event of the ``#mkdocs-search-query`` element |
| 255 | +to call the ``doSearch`` function (available on MkDocs >= 1.x) on every key press. |
| 256 | + |
| 257 | +Read the Docs overrides the ``initSearch`` and ``doSearch`` functions |
| 258 | +to subscribe to the ``keyup`` event of the ``#mkdocs-search-query`` element, |
| 259 | +and puts the results into the ``#mkdocs-search-results`` element. |
| 260 | +A simplified example looks like this: |
| 261 | + |
| 262 | +.. code-block:: js |
| 263 | +
|
| 264 | + var original_search = doSearch; |
| 265 | +
|
| 266 | + function search_override() { |
| 267 | + var query = document.getElementById('mkdocs-search-query').value; |
| 268 | + var search_results = document.getElementById('mkdocs-search-results'); |
| 269 | +
|
| 270 | + var results = fetch_resuls(query); |
| 271 | + if (results) { |
| 272 | + empty_results(search_results) |
| 273 | + for (var i = 0; i < results.length; i += 1) { |
| 274 | + var result = process_result(results[i]); |
| 275 | + append_result(result, search_results); |
| 276 | + } |
| 277 | + } else { |
| 278 | + original_search(); |
| 279 | + } |
| 280 | + } |
| 281 | +
|
| 282 | + var init_override = function () { |
| 283 | + var search_input = document.getElementById('mkdocs-search-query'); |
| 284 | + search_input.addEventListener('keyup', doSearch); |
| 285 | + }; |
| 286 | +
|
| 287 | + window.doSearch = search_override; |
| 288 | + window.initSearch = init_override; |
| 289 | +
|
| 290 | + initSearch(); |
| 291 | +
|
| 292 | +Highlights from results will be in a ``mark`` tag (``This is a <mark>result</mark>``). |
| 293 | +If your theme works with the search plugin of MkDocs, |
| 294 | +and defines the ``#mkdocs-search-query`` and ``#mkdocs-search-results`` elements, |
| 295 | +it will work with Read the Docs' SSS. |
| 296 | + |
| 297 | +.. note:: |
| 298 | + |
| 299 | + Since the ``templates/search/main.js`` file is included after our custom search, |
| 300 | + it will subscribe to the ``keyup`` event too, triggering both functions when a key is pressed |
| 301 | + (but ours should have more precedence). |
| 302 | + This can be fixed by not including the ``search`` plugin (you won't be able to fallback to the original search), |
| 303 | + or by creating a custom plugin to include our search at the end (this should be done by Read the Docs). |
| 304 | + |
| 305 | +.. _`search plugin`: https://www.mkdocs.org/user-guide/configuration/#search |
| 306 | +.. _`templates/search/main.js`: https://github.com/mkdocs/mkdocs/blob/ff0b72/mkdocs/contrib/search/templates/search/main.js |
| 307 | + |
| 308 | +Supporting more themes and static site generators |
| 309 | +------------------------------------------------- |
| 310 | + |
| 311 | +Currently, Read the Docs supports building documentation from |
| 312 | +:doc:`Sphinx </intro/getting-started-with-sphinx>` and :doc:`MkDocs </intro/getting-started-with-mkdocs>`. |
| 313 | +All themes that follow these conventions should work as expected. |
| 314 | +If you think other generators or other conventions should be supported, |
| 315 | +or content that should be ignored or have an especial treatment, |
| 316 | +or if you found an error with our indexing, |
| 317 | +let us know in `our issue tracker`_. |
| 318 | + |
| 319 | +.. _our issue tracker: https://github.com/readthedocs/readthedocs.org/issues/ |
| 320 | + |
| 321 | +.. [*] For Sphinx projects, the content of the main node is provided by an intermediate step in the build process, |
| 322 | + but the HTML components from the node are preserved. |
0 commit comments